Skip to content

Commit

Permalink
test(frontend): Cover practically all the Storybook tests with snapsh…
Browse files Browse the repository at this point in the history
…ots (#14001)
  • Loading branch information
Twixes authored Feb 3, 2023
1 parent ca2d618 commit 406b6d9
Show file tree
Hide file tree
Showing 131 changed files with 169 additions and 126 deletions.
2 changes: 1 addition & 1 deletion .storybook/decorators/withSnapshotsDisabled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DecoratorFn } from '@storybook/react'
/** Workaround for https://github.com/storybookjs/test-runner/issues/74 */
// TODO: Smoke-test all the stories by removing this decorator, once all the stories pass
export const withSnapshotsDisabled: DecoratorFn = (Story, { parameters }) => {
if (parameters?.chromatic?.disableSnapshot && navigator.userAgent.includes('StorybookTestRunner')) {
if (parameters?.testOptions?.skip && navigator.userAgent.includes('StorybookTestRunner')) {
return <>Disabled for Test Runner</>
}
return <Story />
Expand Down
22 changes: 8 additions & 14 deletions .storybook/main.js → .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { createEntry } = require('../webpack.config')
const babelConfig = require('../babel.config')
import type { StorybookConfig } from '@storybook/react/types'
import { createEntry } from '../webpack.config'

module.exports = {
const config: StorybookConfig = {
stories: ['../frontend/src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: [
{
Expand All @@ -19,28 +19,20 @@ module.exports = {
'storybook-addon-pseudo-states',
],
staticDirs: ['public'],
babel: async () => {
// compile babel to "defaults" target (ES5)
const envPreset = babelConfig.presets.find(
(preset) => Array.isArray(preset) && preset[0] === '@babel/preset-env'
)
envPreset[1].targets = 'defaults'
return babelConfig
},
webpackFinal: (config) => {
const mainConfig = createEntry('main')
return {
...config,
resolve: {
...config.resolve,
extensions: [...config.resolve.extensions, ...mainConfig.resolve.extensions],
alias: { ...config.resolve.alias, ...mainConfig.resolve.alias },
extensions: [...config.resolve!.extensions!, ...mainConfig.resolve.extensions],
alias: { ...config.resolve!.alias, ...mainConfig.resolve.alias },
},
module: {
...config.module,
rules: [
...mainConfig.module.rules,
...config.module.rules.filter((rule) => rule.test.toString().includes('.mdx')),
...config.module!.rules.filter((rule) => rule.test!.toString().includes('.mdx')),
{
test: /\.stories\.tsx?$/,
use: [
Expand All @@ -59,3 +51,5 @@ module.exports = {
postcss: false,
},
}

export default config
5 changes: 2 additions & 3 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '~/styles'
import './storybook.scss'
import type { Meta } from '@storybook/react'
import type { Meta, Parameters } from '@storybook/react'
import { worker } from '~/mocks/browser'
import { loadPostHogJS } from '~/loadPostHogJS'
import { getStorybookAppContext } from './app-context'
Expand Down Expand Up @@ -29,7 +29,7 @@ const setupPosthogJs = () => {
setupPosthogJs()

/** Storybook global parameters. See https://storybook.js.org/docs/react/writing-stories/parameters#global-parameters */
export const parameters = {
export const parameters: Parameters = {
actions: { argTypesRegex: '^on[A-Z].*', disabled: true },
controls: {
matchers: {
Expand All @@ -51,7 +51,6 @@ export const parameters = {
'Filters',
'Layout',
],
includeName: true,
},
},
viewMode: 'docs',
Expand Down
47 changes: 37 additions & 10 deletions .storybook/test-runner.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
import { toMatchImageSnapshot } from 'jest-image-snapshot'
import { OptionsParameter } from '@storybook/addons'
import { getStoryContext, TestRunnerConfig, TestContext } from '@storybook/test-runner'
import { Locator, Page, LocatorScreenshotOptions } from 'playwright-core'
import type { Locator, Page, LocatorScreenshotOptions } from 'playwright-core'
import type { Mocks } from '~/mocks/utils'

// Extend Storybook interface `Parameters` with Chromatic parameters
declare module '@storybook/react' {
interface Parameters {
options?: OptionsParameter
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). **/
skip?: boolean
/**
* Whether navigation (sidebar + topbar) should be excluded from the snapshot.
* Warning: Fails if enabled for stories in which navigation is not present.
*/
excludeNavigationFromSnapshot?: boolean
}
mockDate?: string | number | Date
msw?: {
mocks?: Mocks
}
[name: string]: any
}
}

const RETRY_TIMES = 3

const customSnapshotsDir = `${process.cwd()}/frontend/__snapshots__`
const updateSnapshot = expect.getState().snapshotState._updateSnapshot === 'all'

async function expectStoryToMatchFullPageSnapshot(page: Page, context: TestContext): Promise<void> {
await expectLocatorToMatchStorySnapshot(page, context)
Expand All @@ -15,13 +42,11 @@ async function expectStoryToMatchSceneSnapshot(page: Page, context: TestContext)
async function expectStoryToMatchComponentSnapshot(page: Page, context: TestContext): Promise<void> {
await page.evaluate(() => {
const rootEl = document.getElementById('root')

if (rootEl) {
// don't expand the container element to limit the screenshot
// to the component's size
rootEl.style.display = 'inline-block'
}

// make the body transparent to take the screenshot
// without background
document.body.style.background = 'transparent'
Expand All @@ -35,7 +60,7 @@ async function expectLocatorToMatchStorySnapshot(
context: TestContext,
options?: LocatorScreenshotOptions
): Promise<void> {
const image = await locator.screenshot(options)
const image = await locator.screenshot({ timeout: 3000, ...options })
expect(image).toMatchImageSnapshot({
customSnapshotsDir,
customSnapshotIdentifier: context.id,
Expand All @@ -45,7 +70,7 @@ async function expectLocatorToMatchStorySnapshot(
module.exports = {
setup() {
expect.extend({ toMatchImageSnapshot })
jest.retryTimes(3, { logErrorsBeforeRetry: true })
jest.retryTimes(RETRY_TIMES, { logErrorsBeforeRetry: true })
},
async postRender(page, context) {
const storyContext = await getStoryContext(page, context)
Expand All @@ -55,13 +80,13 @@ module.exports = {
document.body.classList.add('dangerously-stop-all-animations')
})

if (!storyContext.parameters?.chromatic?.disableSnapshot) {
if (!storyContext.parameters?.testOptions?.skip) {
let expectStoryToMatchSnapshot: (page: Page, context: TestContext) => Promise<void>
if (storyContext.parameters?.layout === 'fullscreen') {
if (storyContext.parameters.testRunner?.includeNavigation) {
expectStoryToMatchSnapshot = expectStoryToMatchFullPageSnapshot
} else {
if (storyContext.parameters.testOptions?.excludeNavigationFromSnapshot) {
expectStoryToMatchSnapshot = expectStoryToMatchSceneSnapshot
} else {
expectStoryToMatchSnapshot = expectStoryToMatchFullPageSnapshot
}
} else {
expectStoryToMatchSnapshot = expectStoryToMatchComponentSnapshot
Expand All @@ -70,7 +95,9 @@ module.exports = {
// You'd expect that the 'load' state which @storybook/test-runner waits for would already mean
// the story is ready, and definitely that 'networkidle' would indicate all assets to be ready.
// But that's not the case, so we need to introduce a bit of a delay.
await page.waitForTimeout(200)
// The delay is extended when updating snapshots, so that we're 100% sure they represent the final state.
const delayMultiplier: number = updateSnapshot ? RETRY_TIMES : 1
await page.waitForTimeout(250 * delayMultiplier)
await expectStoryToMatchSnapshot(page, context) // Don't retry when updating
}
},
Expand Down
2 changes: 0 additions & 2 deletions Dockerfile.playwright
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,3 @@ RUN pnpm install
COPY playwright.config.ts webpack.config.js babel.config.js tsconfig.json ./

COPY .storybook/ .storybook/

COPY frontend/ frontend/
1 change: 1 addition & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
{
useBuiltIns: 'usage',
corejs: 3,
targets: 'defaults', // browserlist's defaults - https://github.com/browserslist/browserslist#full-list
},
],
[
Expand Down
4 changes: 3 additions & 1 deletion docker-compose.playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ services:
dockerfile: Dockerfile.playwright
network_mode: host
volumes:
- './frontend/__snapshots__:/work/frontend/__snapshots__'
# Mount the whole frontend/ directory to avoid to a new Docker image being generated each time
# frontend code changes. Also, this allows frontend/__snapshots__/ to be updated.
- './frontend:/work/frontend'
- './playwright:/work/playwright'
- './playwright-report:/work/playwright-report'
- './test-results:/work/test-results'
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion frontend/src/layout/navigation/Navigation.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export default {
layout: 'fullscreen',
options: { showPanel: false },
viewMode: 'story',
testRunner: { includeNavigation: true },
},
} as Meta

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity'
export default {
title: 'Components/ActivityLog',
component: ActivityLog,
parameters: { chromatic: { disableSnapshot: true } }, // FIXME: Currently disabled as the Timeout story is flaky
parameters: { testOptions: { skip: true } }, // FIXME: Currently disabled as the Timeout story is flaky
decorators: [
mswDecorator({
get: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default {
'Animations are [LottieFiles.com](https://lottiefiles.com/) animations that we load asynchronously.',
},
},
chromatic: { disableSnapshot: true },
testOptions: { skip: true }, // Animations aren't particularly snapshotable
},
argTypes: {
size: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default {
title: 'Components/Hedgehog Buddy',
component: HedgehogBuddy,
parameters: {
chromatic: { disableSnapshot: true },
testOptions: { skip: true }, // Hedgehogs aren't particularly snapshotable
},
} as ComponentMeta<typeof HedgehogBuddy>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import { personPropertiesModel } from '~/models/personPropertiesModel'
export default {
title: 'Filters/PropertyFilters',
component: PropertyFilters,
parameters: {
chromatic: { disableSnapshot: true },
},
} as ComponentMeta<typeof PropertyFilters>

const propertyFilters = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import { cohortsModel } from '~/models/cohortsModel'
export default {
title: 'Filters/PropertyGroupFilters',
component: PropertyGroupFilters,
parameters: {
chromatic: { disableSnapshot: true },
},
} as ComponentMeta<typeof PropertyGroupFilters>

const propertyFilters = [
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/components/PropertyIcon.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default {
title: 'Lemon UI/Icons/Property Icon',
component: PropertyIcon,
parameters: {
chromatic: { disableSnapshot: true }, // There are too many icons, the snapshots get very big in table form
testOptions: { skip: true }, // There are too many icons, the snapshots are huge in table form
},
} as ComponentMeta<typeof PropertyIcon>

Expand Down
1 change: 0 additions & 1 deletion frontend/src/lib/components/PropertyKeyInfo.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { PropertyKeyInfo } from './PropertyKeyInfo'
export default {
title: 'Components/Property Key Info',
component: PropertyKeyInfo,
chromatic: { disableSnapshot: true },
} as ComponentMeta<typeof PropertyKeyInfo>

const Template: ComponentStory<typeof PropertyKeyInfo> = (args) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export default {
layout: 'fullscreen',
options: { showPanel: false },
viewMode: 'story',
chromatic: { disableSnapshot: true },
},
} as ComponentMeta<typeof SharingModal>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default {
layout: 'fullscreen',
options: { showPanel: false },
viewMode: 'story',
chromatic: { disableSnapshot: true },
mockDate: '2023-01-31 12:00:00',
},
} as ComponentMeta<typeof SubscriptionsModal>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default {
title: 'Filters',
decorators: [taxonomicFilterMocksDecorator],
parameters: {
chromatic: { disableSnapshot: true }, // FIXME: This is currently excluded due to flaky loading of data in it
testOptions: { skip: true }, // FIXME: This is currently excluded due to flaky loading of data in it
},
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/components/hedgehogs.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default {
title: 'Lemon UI/Hog illustrations',
parameters: {
options: { showPanel: false },
chromatic: { disableSnapshot: true },
testOptions: { skip: true }, // Not valuable to take snapshots of these hedgehogs
docs: {
description: {
component: `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import { AlertMessage, AlertMessageProps } from './AlertMessage'
export default {
title: 'Lemon UI/Alert Message',
component: AlertMessage,
// See https://github.com/storybookjs/addon-smart-knobs/issues/63#issuecomment-995798227
parameters: { actions: { argTypesRegex: null }, chromatic: { disableSnapshot: false } },
parameters: {
actions: {
// See https://github.com/storybookjs/addon-smart-knobs/issues/63#issuecomment-995798227
argTypesRegex: null,
},
},
} as ComponentMeta<typeof AlertMessage>

const Template: ComponentStory<typeof AlertMessage> = (props: AlertMessageProps) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { SurprisedHog, BlushingHog } from 'lib/components/hedgehogs'
export default {
title: 'Lemon UI/Lemon Select',
component: LemonSelect,
parameters: { chromatic: { disableSnapshot: false } },
argTypes: {
options: {
defaultValue: [
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/lemon-ui/Popover/Popover.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export default {
title: 'Lemon UI/Popover',
component: Popover,
parameters: {
chromatic: {
disableSnapshot: true, // FIXME: This story needs a play test for the popover to show up in snapshots
testOptions: {
skip: true, // FIXME: This story needs a play test for the popup to show up in snapshots
},
},
} as ComponentMeta<typeof Popover>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/lemon-ui/icons/icons.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default {
title: 'Lemon UI/Icons',
parameters: {
options: { showPanel: false },
chromatic: { disableSnapshot: true }, // There are too many icons, the snapshots get very big in table form
testOptions: { skip: true }, // There are too many icons, the snapshots are huge in table form
docs: {
description: {
component: `
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/mocks/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const mswDecorator = (mocks: Mocks): DecoratorFunction<JSX.Element> => {
for (const restMethod of Object.keys(rest)) {
mergedMocks[restMethod] = {}
// Ensure trailing slashes to avoid default handlers accidentally overshadowing story mocks
for (const [path, handler] of Object.entries(parameters.msw.mocks?.[restMethod] || {})) {
for (const [path, handler] of Object.entries(parameters.msw?.mocks?.[restMethod] || {})) {
const cleanedPath = path.replace(/\/?$/, '/')
mergedMocks[restMethod][cleanedPath] = handler
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/mocks/fixtures/_preflight.json
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@
"is_debug": true,
"is_event_property_usage_enabled": true,
"licensed_users_available": 21311,
"site_url": "http://localhost:8000",
"site_url": "http://localhost:6006",
"instance_preferences": {
"debug_queries": false,
"disable_paid_fs": false
Expand Down
Loading

0 comments on commit 406b6d9

Please sign in to comment.