Skip to content

Commit

Permalink
Merge branch 'master' into stylelint-order
Browse files Browse the repository at this point in the history
  • Loading branch information
Twixes committed Nov 23, 2023
2 parents 19aa014 + c0c0f30 commit 3c1d2a4
Show file tree
Hide file tree
Showing 303 changed files with 5,283 additions and 4,430 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ jobs:
# NOTE: we are at risk of missing a dependency here. We could make
# the dependencies more clear if we separated the backend/frontend
# code completely
# really we should ignore ee/frontend/** but dorny doesn't support that
- 'ee/**/*'
- '!ee/frontend/**'
- 'posthog/**/*'
- 'bin/*.py'
- requirements.txt
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/ci-frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
# NOTE: we are at risk of missing a dependency here.
- 'bin/**'
- 'frontend/**'
- 'ee/frontend/**'
# Make sure we run if someone is explicitly change the workflow
- .github/workflows/ci-frontend.yml
# various JS config files
Expand Down Expand Up @@ -117,18 +118,23 @@ jobs:
jest:
runs-on: ubuntu-latest
needs: changes
name: Jest test (${{ matrix.chunk }})
name: Jest test (${{ matrix.segment }} - ${{ matrix.chunk }})

strategy:
# If one test fails, still run the others
fail-fast: false
matrix:
segment: ['FOSS', 'EE']
chunk: [1, 2, 3]

steps:
# we need at least one thing to run to make sure we include everything for required jobs
- uses: actions/checkout@v3

- name: Remove ee
if: needs.changes.outputs.frontend == 'true' && matrix.segment == 'FOSS'
run: rm -rf ee

- name: Install pnpm
if: needs.changes.outputs.frontend == 'true'
uses: pnpm/action-setup@v2
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/container-images-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ jobs:
message: |
{
"image_tag": "${{ steps.build.outputs.digest }}",
"image_tag_unit": "${{ steps.build-unit.outputs.digest }}",
"context": ${{ toJson(github) }}
}
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/storybook-chromatic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
container:
image: mcr.microsoft.com/playwright:v1.29.2 # Same as @storybook/test-runner@0.13's dependency
image: mcr.microsoft.com/playwright:v1.32.2 # Same as @storybook/test-runner@0.15.2's dependency
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -144,6 +144,7 @@ jobs:
HOME: /root
# Update snapshots for PRs on the main repo, verify on forks, which don't have access to PostHog Bot
VARIANT: ${{ github.event.pull_request.head.repo.full_name == github.repository && 'update' || 'verify' }}
STORYBOOK_SKIP_TAGS: 'test-skip,test-skip-${{ matrix.browser }}'
run: |
pnpm test:visual-regression:stories:ci:$VARIANT --browsers ${{ matrix.browser }} --shard ${{ matrix.shard }}/$SHARD_COUNT
Expand Down
11 changes: 0 additions & 11 deletions .storybook/decorators/withSnapshotsDisabled.tsx

This file was deleted.

2 changes: 0 additions & 2 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { getStorybookAppContext } from './app-context'
import { withKea } from './decorators/withKea'
import { withMockDate } from './decorators/withMockDate'
import { defaultMocks } from '~/mocks/handlers'
import { withSnapshotsDisabled } from './decorators/withSnapshotsDisabled'
import { withFeatureFlags } from './decorators/withFeatureFlags'
import { withTheme } from './decorators/withTheme'

Expand Down Expand Up @@ -79,7 +78,6 @@ export const parameters: Parameters = {

// Setup storybook global decorators. See https://storybook.js.org/docs/react/writing-stories/decorators#global-decorators
export const decorators: Meta['decorators'] = [
withSnapshotsDisabled,
// Make sure the msw service worker is started, and reset the handlers to defaults.
withKea,
// Allow us to time travel to ensure our stories don't change over time.
Expand Down
86 changes: 57 additions & 29 deletions .storybook/test-runner.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import { toMatchImageSnapshot } from 'jest-image-snapshot'
import { getStoryContext, TestRunnerConfig, TestContext } from '@storybook/test-runner'
import type { Locator, Page, LocatorScreenshotOptions } from 'playwright-core'
import { getStoryContext, TestRunnerConfig, TestContext, waitForPageReady } from '@storybook/test-runner'
import type { Locator, Page, LocatorScreenshotOptions } from '@playwright/test'
import type { Mocks } from '~/mocks/utils'
import { StoryContext } from '@storybook/types'
import { StoryContext } from '@storybook/csf'

// 'firefox' is technically supported too, but as of June 2023 it has memory usage issues that make is unusable
type SupportedBrowserName = 'chromium' | 'webkit'
type SnapshotTheme = 'legacy' | 'light' | 'dark'

// Extend Storybook interface `Parameters` with Chromatic parameters
declare module '@storybook/types' {
interface Parameters {
options?: any
layout?: 'padded' | 'fullscreen' | 'centered'
testOptions?: {
/**
* Whether the test should be a no-op (doesn't jest.skip as @storybook/test-runner doesn't allow that).
* @default false
*/
skip?: boolean
/**
* Whether we should wait for all loading indicators to disappear before taking a snapshot.
* @default true
Expand All @@ -40,12 +36,18 @@ declare module '@storybook/types' {
snapshotBrowsers?: SupportedBrowserName[]
/** If taking a component snapshot, you can narrow it down by specifying the selector. */
snapshotTargetSelector?: string
/** Include snapshots of buttons in 3000. */
include3000?: boolean
}
msw?: {
mocks?: Mocks
}
[name: string]: any
}

interface Globals {
theme: SnapshotTheme
}
}

const RETRY_TIMES = 3
Expand All @@ -71,19 +73,20 @@ module.exports = {
jest.retryTimes(RETRY_TIMES, { logErrorsBeforeRetry: true })
jest.setTimeout(JEST_TIMEOUT_MS)
},
async postRender(page, context) {
async postVisit(page, context) {
const browserContext = page.context()
const storyContext = (await getStoryContext(page, context)) as StoryContext
const { skip = false, snapshotBrowsers = ['chromium'] } = storyContext.parameters?.testOptions ?? {}
const storyContext = await getStoryContext(page, context)
const { snapshotBrowsers = ['chromium'] } = storyContext.parameters?.testOptions ?? {}

browserContext.setDefaultTimeout(PLAYWRIGHT_TIMEOUT_MS)
if (!skip) {
const currentBrowser = browserContext.browser()!.browserType().name() as SupportedBrowserName
if (snapshotBrowsers.includes(currentBrowser)) {
await expectStoryToMatchSnapshot(page, context, storyContext, currentBrowser)
}
const currentBrowser = browserContext.browser()!.browserType().name() as SupportedBrowserName
if (snapshotBrowsers.includes(currentBrowser)) {
await expectStoryToMatchSnapshot(page, context, storyContext, currentBrowser)
}
},
tags: {
skip: ['test-skip'], // NOTE: This is overridden by the CI action storybook-chromatic.yml to include browser specific skipping
},
} as TestRunnerConfig

async function expectStoryToMatchSnapshot(
Expand All @@ -96,12 +99,14 @@ async function expectStoryToMatchSnapshot(
waitForLoadersToDisappear = true,
waitForSelector,
excludeNavigationFromSnapshot = false,
include3000 = false,
} = storyContext.parameters?.testOptions ?? {}

let check: (
page: Page,
context: TestContext,
browser: SupportedBrowserName,
theme: SnapshotTheme,
targetSelector?: string
) => Promise<void>
if (storyContext.parameters?.layout === 'fullscreen') {
Expand All @@ -114,8 +119,7 @@ async function expectStoryToMatchSnapshot(
check = expectStoryToMatchComponentSnapshot
}

// Wait for story to load
await page.waitForSelector('.sb-show-preparing-story', { state: 'detached' })
await waitForPageReady(page)
await page.evaluate(() => {
// Stop all animations for consistent snapshots
document.body.classList.add('storybook-test-runner')
Expand All @@ -127,44 +131,62 @@ async function expectStoryToMatchSnapshot(
await page.waitForSelector(waitForSelector)
}

await page.waitForTimeout(400) // Wait for animations to finish
await page.waitForTimeout(400) // Wait for effects to finish

// Wait for all images to load
await page.waitForFunction(() =>
Array.from(document.querySelectorAll('img')).every((i: HTMLImageElement) => i.complete)
)

await check(page, context, browser, storyContext.parameters?.testOptions?.snapshotTargetSelector)
await check(page, context, browser, 'legacy', storyContext.parameters?.testOptions?.snapshotTargetSelector)

if (include3000) {
await page.evaluate(() => {
document.body.classList.add('posthog-3000')
document.body.setAttribute('theme', 'light')
})

await check(page, context, browser, 'light', storyContext.parameters?.testOptions?.snapshotTargetSelector)

await page.evaluate(() => {
document.body.setAttribute('theme', 'dark')
})

await check(page, context, browser, 'dark', storyContext.parameters?.testOptions?.snapshotTargetSelector)
}
}

async function expectStoryToMatchFullPageSnapshot(
page: Page,
context: TestContext,
browser: SupportedBrowserName
browser: SupportedBrowserName,
theme: SnapshotTheme
): Promise<void> {
await expectLocatorToMatchStorySnapshot(page, context, browser)
await expectLocatorToMatchStorySnapshot(page, context, browser, theme)
}

async function expectStoryToMatchSceneSnapshot(
page: Page,
context: TestContext,
browser: SupportedBrowserName
browser: SupportedBrowserName,
theme: SnapshotTheme
): Promise<void> {
await page.evaluate(() => {
// The screenshot gets clipped by the overflow hidden of the sidebar
document.querySelector('.SideBar')?.setAttribute('style', 'overflow: visible;')
})

await expectLocatorToMatchStorySnapshot(page.locator('.main-app-content'), context, browser)
await expectLocatorToMatchStorySnapshot(page.locator('.main-app-content'), context, browser, theme)
}

async function expectStoryToMatchComponentSnapshot(
page: Page,
context: TestContext,
browser: SupportedBrowserName,
theme: SnapshotTheme,
targetSelector: string = '#storybook-root'
): Promise<void> {
await page.evaluate(() => {
await page.evaluate((theme) => {
const rootEl = document.getElementById('storybook-root')
if (!rootEl) {
throw new Error('Could not find root element')
Expand All @@ -188,21 +210,27 @@ async function expectStoryToMatchComponentSnapshot(
rootEl.style.width = `${-popoverBoundingClientRect.left + currentRootBoundingClientRect.right}px`
}
})
// Make the body transparent to take the screenshot without background
document.body.style.background = 'transparent'
})
// For legacy style, make the body transparent to take the screenshot without background
document.body.style.background = theme === 'legacy' ? 'transparent' : 'var(--bg-3000)'
}, theme)

await expectLocatorToMatchStorySnapshot(page.locator(targetSelector), context, browser, { omitBackground: true })
await expectLocatorToMatchStorySnapshot(page.locator(targetSelector), context, browser, theme, {
omitBackground: true,
})
}

async function expectLocatorToMatchStorySnapshot(
locator: Locator | Page,
context: TestContext,
browser: SupportedBrowserName,
theme: SnapshotTheme,
options?: LocatorScreenshotOptions
): Promise<void> {
const image = await locator.screenshot({ ...options })
let customSnapshotIdentifier = context.id
if (theme !== 'legacy') {
customSnapshotIdentifier += `--${theme}`
}
if (browser !== 'chromium') {
customSnapshotIdentifier += `--${browser}`
}
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.playwright
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# We do this to ensure our reference images for visual regression tests are the same during development and in CI.
#

FROM mcr.microsoft.com/playwright:v1.29.2
FROM mcr.microsoft.com/playwright:v1.32.2

WORKDIR /work

Expand Down
54 changes: 54 additions & 0 deletions cypress/e2e/surveys.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,58 @@ describe('Surveys', () => {
cy.get('[data-attr=delete-survey]').click()
cy.get('.Toastify__toast-body').contains('Survey deleted').should('be.visible')
})

it('creates a new multiple choice survey with an open-ended choice', () => {
cy.get('h1').should('contain', 'Surveys')
cy.get('[data-attr=new-survey]').click()
cy.get('[data-attr=new-blank-survey]').click()

// add a multiple choice question with an open-ended question
cy.get('[data-attr=survey-name]').focus().type(name).should('have.value', name)
cy.get('[data-attr="survey-question-type-0"]').click()
cy.contains('Multiple choice select').click()
cy.get('button').contains('Add open-ended choice').click()

// check default open-ended choice form input and appearance after
// open-ended choice was added
cy.get('.LemonInput__input[value="Other"]')
cy.get('.choice-option').eq(3).contains('Other:')
cy.get('.choice-option').eq(3).find('input[type="text"]').should('have.value', '')

// typing in open-ended question's appearance automatically checks the
// checkbox
cy.get('.choice-option').eq(3).find('input[type="checkbox"]').should('not.be.checked')
cy.get('.choice-option').eq(3).find('input[type="text"]').type('Outreach')
cy.get('.choice-option').eq(3).find('input[type="checkbox"]').should('be.checked')

// clicking on open-ended question's appearance label unchecks or checks
// the checkbox
cy.get('.choice-option').eq(3).click()
cy.get('.choice-option').eq(3).find('input[type="checkbox"]').should('not.be.checked')
cy.get('.choice-option').eq(3).click()
cy.get('.choice-option').eq(3).find('input[type="checkbox"]').should('be.checked')

// removing text in open-ended question's appearance automatically
// unchecks the checkbox
cy.get('.choice-option').eq(3).find('input[type="text"]').clear()

// open-ended question label doesn't change even if appearance input
// changes
cy.get('.LemonInput__input[value="Other"]')

// change open-ended choice after the label was added
cy.contains('Choices').parent().find('input[type="text"]').eq(3).clear()
cy.contains('Choices').parent().find('input[type="text"]').eq(3).type('First Choice')
cy.get('.choice-option').eq(3).contains('First Choice:')

// attempt to create and save survey
cy.get('[data-attr=save-survey]').first().click()

// after save there should be a launch button
cy.get('button[data-attr="launch-survey"]').should('have.text', 'Launch')

cy.clickNavMenu('surveys')
cy.get('[data-attr=surveys-table]').should('contain', name)
cy.get(`[data-row-key="${name}"]`).contains(name).click()
})
})
2 changes: 1 addition & 1 deletion cypress/e2e/toolbar.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('Toolbar', () => {
.then((href) => {
cy.visit(href)
})
cy.get('#__POSTHOG_TOOLBAR__').shadow().find('.floating-toolbar-button').should('exist')
cy.get('#__POSTHOG_TOOLBAR__').shadow().find('.Toolbar').should('exist')
})
})

Expand Down
1 change: 1 addition & 0 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ beforeEach(() => {
'surveys-new-creation-flow': true,
'surveys-results-visualizations': true,
'auto-redirect': true,
'surveys-open-choice': true,
notebooks: true,
})
)
Expand Down
13 changes: 13 additions & 0 deletions ee/frontend/exports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { PostHogEE } from '@posthog/ee/types'

const myTestCode = (): void => {
// eslint-disable-next-line no-console
console.log('it works!')
}

const postHogEE: PostHogEE = {
enabled: true,
myTestCode,
}

export default postHogEE
Loading

0 comments on commit 3c1d2a4

Please sign in to comment.