mirror of
https://github.com/PostHog/posthog.git
synced 2024-11-21 21:49:51 +01:00
test: Regular checkup of visual regression tests (#18469)
* test: Regular checkup of visual regression tests * Fix billing gauge animation Animations done in JS can't be stopped automatically by the Storybook test runner. CSS animations can, easily, and they are anyway the cleaner and more performant way of achieving the same here. * Rename misleading feature flag `recentInsights` to `relatedInsights` * Mock homepage endpoints to avoid error toasts * Wait for the recordings list in the notebook node story * Fix `featureFlagLogic` * Wait for `.NotebookNode__content` * Try to optimize * Screenshot failures and upload as artifacts * Fix remaining failures * Increase timeouts * Fix rendering of Survey stories * Remove `clang-format` * Update UI snapshots for `chromium` (2) * Update UI snapshots for `chromium` (2) * Update UI snapshots for `chromium` (2) * Fix alignment of series name in insights details * Try to fix experiment story flakiness * Include toasts in loaders * Fix superfluous toast * Fix un-awaited breakpoints * Update UI snapshots for `chromium` (1) * Update UI snapshots for `chromium` (1) * Update UI snapshots for `chromium` (1) * Make login snapshots slightly stabler * Update UI snapshots for `chromium` (2) * Update UI snapshots for `chromium` (2) * Skip incorrect Surveys story * Update UI snapshots for `chromium` (2) * Revert msw upgrade --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
8ee0ac490e
commit
fded6fdf62
@ -36,3 +36,5 @@
|
||||
!unit.json
|
||||
!plugin-transpiler/src
|
||||
!plugin-transpiler/*.*
|
||||
!test-runner-jest.config.js
|
||||
!test-runner-jest-environment.js
|
||||
|
19
.github/workflows/storybook-chromatic.yml
vendored
19
.github/workflows/storybook-chromatic.yml
vendored
@ -64,7 +64,6 @@ jobs:
|
||||
SHARD_COUNT: '2'
|
||||
CYPRESS_INSTALL_BINARY: '0'
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
JEST_IMAGE_SNAPSHOT_TRACK_OBSOLETE: '1' # Remove obsolete snapshots
|
||||
OPT_OUT_CAPTURE: 1
|
||||
outputs:
|
||||
# The below have to be manually listed unfortunately, as GitHub Actions doesn't allow matrix-dependent outputs
|
||||
@ -132,7 +131,7 @@ jobs:
|
||||
retries=3
|
||||
while [ $retries -gt 0 ]; do
|
||||
pnpm exec http-server storybook-static --port 6006 --silent &
|
||||
if pnpm wait-on http://127.0.0.1:6006 --timeout 60; then
|
||||
if pnpm wait-on http://127.0.0.1:6006 --timeout 15; then
|
||||
break
|
||||
fi
|
||||
retries=$((retries-1))
|
||||
@ -146,14 +145,7 @@ jobs:
|
||||
# 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' }}
|
||||
run: |
|
||||
retries=3
|
||||
while [ $retries -gt 0 ]; do
|
||||
if pnpm test:visual-regression:stories:ci:$VARIANT --browsers ${{ matrix.browser }} --shard ${{ matrix.shard }}/$SHARD_COUNT; then
|
||||
break
|
||||
fi
|
||||
retries=$((retries-1))
|
||||
echo "Failed @storybook/test-runner, retrying... ($retries retries left)"
|
||||
done
|
||||
pnpm test:visual-regression:stories:ci:$VARIANT --browsers ${{ matrix.browser }} --shard ${{ matrix.shard }}/$SHARD_COUNT
|
||||
|
||||
- name: Run @playwright/test (legacy, Chromium-only)
|
||||
if: matrix.browser == 'chromium' && matrix.shard == 1
|
||||
@ -163,6 +155,13 @@ jobs:
|
||||
run: |
|
||||
pnpm test:visual-regression:legacy:ci:$VARIANT
|
||||
|
||||
- name: Archive failure screenshots
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: failure-screenshots-${{ matrix.browser }}
|
||||
path: frontend/__snapshots__/__failures__/
|
||||
|
||||
- name: Count and optimize updated snapshots
|
||||
id: diff
|
||||
# Skip on forks
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,6 +18,7 @@ frontend/.cache/
|
||||
frontend/dist/
|
||||
frontend/types/
|
||||
frontend/__snapshots__/__diff_output__/
|
||||
frontend/__snapshots__/__failures__/
|
||||
*Type.ts
|
||||
frontend/pnpm-error.log
|
||||
frontend/tmp
|
||||
|
@ -48,36 +48,35 @@ declare module '@storybook/types' {
|
||||
}
|
||||
}
|
||||
|
||||
const RETRY_TIMES = 5
|
||||
const RETRY_TIMES = 3
|
||||
const LOADER_SELECTORS = [
|
||||
'.ant-skeleton',
|
||||
'.Spinner',
|
||||
'.LemonSkeleton',
|
||||
'.LemonTableLoader',
|
||||
'.Toastify__toast-container',
|
||||
'[aria-busy="true"]',
|
||||
'[aria-label="Content is loading..."]',
|
||||
'.SessionRecordingPlayer--buffering',
|
||||
'.Lettermark--unknown',
|
||||
]
|
||||
|
||||
const customSnapshotsDir = `${process.cwd()}/frontend/__snapshots__`
|
||||
|
||||
const TEST_TIMEOUT_MS = 10000
|
||||
const BROWSER_DEFAULT_TIMEOUT_MS = 9000 // Reduce the default timeout down from 30s, to pre-empt Jest timeouts
|
||||
const SCREENSHOT_TIMEOUT_MS = 9000
|
||||
const JEST_TIMEOUT_MS = 15000
|
||||
const PLAYWRIGHT_TIMEOUT_MS = 10000 // Must be shorter than JEST_TIMEOUT_MS
|
||||
|
||||
module.exports = {
|
||||
setup() {
|
||||
expect.extend({ toMatchImageSnapshot })
|
||||
jest.retryTimes(RETRY_TIMES, { logErrorsBeforeRetry: true })
|
||||
jest.setTimeout(TEST_TIMEOUT_MS)
|
||||
jest.setTimeout(JEST_TIMEOUT_MS)
|
||||
},
|
||||
async postRender(page, context) {
|
||||
const browserContext = page.context()
|
||||
const storyContext = (await getStoryContext(page, context)) as StoryContext
|
||||
const { skip = false, snapshotBrowsers = ['chromium'] } = storyContext.parameters?.testOptions ?? {}
|
||||
|
||||
browserContext.setDefaultTimeout(BROWSER_DEFAULT_TIMEOUT_MS)
|
||||
browserContext.setDefaultTimeout(PLAYWRIGHT_TIMEOUT_MS)
|
||||
if (!skip) {
|
||||
const currentBrowser = browserContext.browser()!.browserType().name() as SupportedBrowserName
|
||||
if (snapshotBrowsers.includes(currentBrowser)) {
|
||||
@ -202,7 +201,7 @@ async function expectLocatorToMatchStorySnapshot(
|
||||
browser: SupportedBrowserName,
|
||||
options?: LocatorScreenshotOptions
|
||||
): Promise<void> {
|
||||
const image = await locator.screenshot({ timeout: SCREENSHOT_TIMEOUT_MS, ...options })
|
||||
const image = await locator.screenshot({ ...options })
|
||||
let customSnapshotIdentifier = context.id
|
||||
if (browser !== 'chromium') {
|
||||
customSnapshotIdentifier += `--${browser}`
|
||||
|
@ -15,6 +15,6 @@ ENV CYPRESS_INSTALL_BINARY=0
|
||||
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
COPY playwright.config.ts webpack.config.js babel.config.js tsconfig.json ./
|
||||
COPY playwright.config.ts webpack.config.js babel.config.js tsconfig.json test-runner-jest.config.js test-runner-jest-environment.js ./
|
||||
|
||||
COPY .storybook/ .storybook/
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 46 KiB |
Binary file not shown.
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 107 KiB |
@ -137,7 +137,7 @@ export const activationLogic = kea<activationLogicType>([
|
||||
0,
|
||||
{
|
||||
loadCustomEvents: async (_, breakpoint) => {
|
||||
breakpoint(200)
|
||||
await breakpoint(200)
|
||||
const url = api.eventDefinitions.determineListEndpoint({
|
||||
event_type: EventDefinitionType.EventCustom,
|
||||
})
|
||||
|
@ -127,6 +127,7 @@
|
||||
|
||||
.SeriesDisplay__raw-name {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.125rem 0.25rem;
|
||||
margin: 0 0.25rem;
|
||||
background: var(--primary-bg-hover);
|
||||
@ -135,12 +136,12 @@
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
line-height: 1rem;
|
||||
vertical-align: -0.3em;
|
||||
&.SeriesDisplay__raw-name--action,
|
||||
&.SeriesDisplay__raw-name--event {
|
||||
padding: 0.25rem;
|
||||
&::before {
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
width: 1rem;
|
||||
border-radius: 0.25rem;
|
||||
@ -169,5 +170,4 @@
|
||||
margin-right: 0.25rem;
|
||||
color: var(--border-bold);
|
||||
font-size: 1.25rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
@ -14,6 +14,11 @@ const meta: Meta<typeof Map> = {
|
||||
center: coordinates,
|
||||
className: 'h-60',
|
||||
},
|
||||
parameters: {
|
||||
testOptions: {
|
||||
skip: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
type Story = StoryObj<typeof Map>
|
||||
|
||||
|
@ -109,7 +109,7 @@ export function TaxonomicPropertyFilter({
|
||||
|
||||
const { ref: wrapperRef, size } = useResizeBreakpoints({
|
||||
0: 'tiny',
|
||||
400: 'small',
|
||||
300: 'small',
|
||||
550: 'medium',
|
||||
})
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { userLogic } from 'scenes/userLogic'
|
||||
import { useMocks } from '~/mocks/jest'
|
||||
import { Scene } from 'scenes/sceneTypes'
|
||||
|
||||
describe('sceneDashboardChoiceModalLogic ', () => {
|
||||
describe('sceneDashboardChoiceModalLogic', () => {
|
||||
let logic: ReturnType<typeof sceneDashboardChoiceModalLogic.build>
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Login.stories.tsx
|
||||
import { Meta } from '@storybook/react'
|
||||
import { Meta, StoryFn } from '@storybook/react'
|
||||
import { Login } from './Login'
|
||||
import { mswDecorator, useStorybookMocks } from '~/mocks/browser'
|
||||
import { useEffect } from 'react'
|
||||
@ -27,7 +27,8 @@ const meta: Meta = {
|
||||
],
|
||||
}
|
||||
export default meta
|
||||
export const Cloud = (): JSX.Element => {
|
||||
|
||||
export const Cloud: StoryFn = () => {
|
||||
useStorybookMocks({
|
||||
get: {
|
||||
'/_preflight': {
|
||||
@ -41,7 +42,8 @@ export const Cloud = (): JSX.Element => {
|
||||
})
|
||||
return <Login />
|
||||
}
|
||||
export const CloudEU = (): JSX.Element => {
|
||||
|
||||
export const CloudEU: StoryFn = () => {
|
||||
useStorybookMocks({
|
||||
get: {
|
||||
'/_preflight': {
|
||||
@ -56,7 +58,8 @@ export const CloudEU = (): JSX.Element => {
|
||||
})
|
||||
return <Login />
|
||||
}
|
||||
export const CloudWithGoogleLoginEnforcement = (): JSX.Element => {
|
||||
|
||||
export const CloudWithGoogleLoginEnforcement: StoryFn = () => {
|
||||
useStorybookMocks({
|
||||
get: {
|
||||
'/_preflight': {
|
||||
@ -78,7 +81,13 @@ export const CloudWithGoogleLoginEnforcement = (): JSX.Element => {
|
||||
}, [])
|
||||
return <Login />
|
||||
}
|
||||
export const SelfHosted = (): JSX.Element => {
|
||||
CloudWithGoogleLoginEnforcement.parameters = {
|
||||
testOptions: {
|
||||
waitForSelector: '[href^="/login/google-oauth2/"]',
|
||||
},
|
||||
}
|
||||
|
||||
export const SelfHosted: StoryFn = () => {
|
||||
useStorybookMocks({
|
||||
get: {
|
||||
'/_preflight': {
|
||||
@ -92,7 +101,7 @@ export const SelfHosted = (): JSX.Element => {
|
||||
return <Login />
|
||||
}
|
||||
|
||||
export const SelfHostedWithSAML = (): JSX.Element => {
|
||||
export const SelfHostedWithSAML: StoryFn = () => {
|
||||
useStorybookMocks({
|
||||
get: {
|
||||
'/_preflight': {
|
||||
@ -105,8 +114,13 @@ export const SelfHostedWithSAML = (): JSX.Element => {
|
||||
})
|
||||
return <Login />
|
||||
}
|
||||
SelfHostedWithSAML.parameters = {
|
||||
testOptions: {
|
||||
waitForSelector: '[href^="/login/saml/"]',
|
||||
},
|
||||
}
|
||||
|
||||
export const SSOError = (): JSX.Element => {
|
||||
export const SSOError: StoryFn = () => {
|
||||
useStorybookMocks({
|
||||
get: {
|
||||
'/_preflight': preflightJson,
|
||||
@ -119,7 +133,7 @@ export const SSOError = (): JSX.Element => {
|
||||
return <Login />
|
||||
}
|
||||
|
||||
export const SecondFactor = (): JSX.Element => {
|
||||
export const SecondFactor: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
// Change the URL
|
||||
router.actions.push(urls.login2FA())
|
||||
|
@ -1,8 +1,5 @@
|
||||
.BillingGauge {
|
||||
}
|
||||
|
||||
.BillingGaugeItem {
|
||||
transition: all 1000ms cubic-bezier(0.15, 0.15, 0.2, 1);
|
||||
animation: billing-gauge-item-expand 800ms cubic-bezier(0.15, 0.15, 0.2, 1) forwards;
|
||||
|
||||
.BillingGaugeItem__info {
|
||||
position: absolute;
|
||||
@ -28,3 +25,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes billing-gauge-item-expand {
|
||||
0% {
|
||||
width: 0%;
|
||||
}
|
||||
100% {
|
||||
width: var(--billing-gauge-item-width);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import clsx from 'clsx'
|
||||
import { Tooltip } from 'lib/lemon-ui/Tooltip'
|
||||
import { compactNumber } from 'lib/utils'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import './BillingGauge.scss'
|
||||
|
||||
type BillingGaugeItemProps = {
|
||||
@ -15,7 +15,10 @@ type BillingGaugeItemProps = {
|
||||
const BillingGaugeItem = ({ width, className, tooltip, top, value }: BillingGaugeItemProps): JSX.Element => {
|
||||
return (
|
||||
// eslint-disable-next-line react/forbid-dom-props
|
||||
<div className={`BillingGaugeItem absolute top-0 left-0 bottom-0 h-2 ${className}`} style={{ width: width }}>
|
||||
<div
|
||||
className={`BillingGaugeItem absolute top-0 left-0 bottom-0 h-2 ${className}`}
|
||||
style={{ '--billing-gauge-item-width': width } as React.CSSProperties}
|
||||
>
|
||||
<div className="absolute right-0 w-px h-full bg-bg-light" />
|
||||
<Tooltip title={value.toLocaleString()} placement={'right'}>
|
||||
<div
|
||||
@ -41,22 +44,16 @@ export type BillingGaugeProps = {
|
||||
}
|
||||
|
||||
export function BillingGauge({ items }: BillingGaugeProps): JSX.Element {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const maxScale = useMemo(() => {
|
||||
return Math.max(100, ...items.map((item) => item.value)) * 1.3
|
||||
}, [items])
|
||||
|
||||
useEffect(() => {
|
||||
// On mount, animate the gauge to full width
|
||||
setExpanded(true)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="relative h-2 bg-border-light my-16">
|
||||
{items.map((item, i) => (
|
||||
<BillingGaugeItem
|
||||
key={i}
|
||||
width={expanded ? `${(item.value / maxScale) * 100}%` : '0%'}
|
||||
width={`${(item.value / maxScale) * 100}%`}
|
||||
className={`bg-${item.color}`}
|
||||
tooltip={<b>{item.text}</b>}
|
||||
top={item.top}
|
||||
|
@ -231,7 +231,7 @@ export const billingLogic = kea<billingLogicType>([
|
||||
license: !license ? 'Please enter your license key' : undefined,
|
||||
}),
|
||||
submit: async ({ license }, breakpoint) => {
|
||||
breakpoint(500)
|
||||
await breakpoint(500)
|
||||
try {
|
||||
await api.update('api/billing-v2/license', {
|
||||
license,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react'
|
||||
import { Meta } from '@storybook/react'
|
||||
import { Meta, StoryFn } from '@storybook/react'
|
||||
import { router } from 'kea-router'
|
||||
import { urls } from 'scenes/urls'
|
||||
import { App } from 'scenes/App'
|
||||
@ -454,7 +454,7 @@ const MOCK_TREND_EXPERIMENT_RESULTS: TrendsExperimentResults = {
|
||||
},
|
||||
aggregated_value: 0,
|
||||
label: '$pageview - control',
|
||||
count: 11.421053, // eslint-disable-line no-loss-of-precision
|
||||
count: 11.421053,
|
||||
data: [
|
||||
2.4210526315789473, 1.4210526315789473, 3.4210526315789473, 0.4210526315789473, 3.4210526315789473,
|
||||
],
|
||||
@ -598,7 +598,7 @@ const meta: Meta = {
|
||||
],
|
||||
}
|
||||
export default meta
|
||||
export function ExperimentsList(): JSX.Element {
|
||||
export const ExperimentsList: StoryFn = () => {
|
||||
useAvailableFeatures([AvailableFeature.EXPERIMENTATION])
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.experiments())
|
||||
@ -606,15 +606,20 @@ export function ExperimentsList(): JSX.Element {
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function CompleteFunnelExperiment(): JSX.Element {
|
||||
export const CompleteFunnelExperiment: StoryFn = () => {
|
||||
useAvailableFeatures([AvailableFeature.EXPERIMENTATION])
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.experiment(MOCK_FUNNEL_EXPERIMENT.id))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
CompleteFunnelExperiment.parameters = {
|
||||
testOptions: {
|
||||
waitForSelector: '.card-secondary',
|
||||
},
|
||||
}
|
||||
|
||||
export function RunningTrendExperiment(): JSX.Element {
|
||||
export const RunningTrendExperiment: StoryFn = () => {
|
||||
useAvailableFeatures([AvailableFeature.EXPERIMENTATION])
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.experiment(MOCK_TREND_EXPERIMENT.id))
|
||||
@ -622,22 +627,27 @@ export function RunningTrendExperiment(): JSX.Element {
|
||||
|
||||
return <App />
|
||||
}
|
||||
RunningTrendExperiment.parameters = {
|
||||
testOptions: {
|
||||
waitForSelector: '.card-secondary',
|
||||
},
|
||||
}
|
||||
|
||||
export function ExperimentsListPayGate(): JSX.Element {
|
||||
export const ExperimentsListPayGate: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.experiments())
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function ViewExperimentPayGate(): JSX.Element {
|
||||
export const ViewExperimentPayGate: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.experiment(MOCK_FUNNEL_EXPERIMENT.id))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function ExperimentNotFound(): JSX.Element {
|
||||
export const ExperimentNotFound: StoryFn = () => {
|
||||
useAvailableFeatures([AvailableFeature.EXPERIMENTATION])
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.experiment('1200000'))
|
||||
|
@ -6,11 +6,11 @@ import { InsightModel } from '~/types'
|
||||
import { featureFlagLogic } from './featureFlagLogic'
|
||||
|
||||
export function RecentFeatureFlagInsights(): JSX.Element {
|
||||
const { recentInsights, recentInsightsLoading, featureFlag } = useValues(featureFlagLogic)
|
||||
const { relatedInsights, relatedInsightsLoading, featureFlag } = useValues(featureFlagLogic)
|
||||
return (
|
||||
<CompactList
|
||||
title="Insights that use this feature flag"
|
||||
loading={recentInsightsLoading}
|
||||
loading={relatedInsightsLoading}
|
||||
emptyMessage={{
|
||||
title: 'You have no insights that use this feature flag',
|
||||
description: "Explore this feature flag's insights by creating one below.",
|
||||
@ -21,7 +21,7 @@ export function RecentFeatureFlagInsights(): JSX.Element {
|
||||
breakdown: `$feature/${featureFlag.key}`,
|
||||
}),
|
||||
}}
|
||||
items={recentInsights.slice(0, 5)}
|
||||
items={relatedInsights.slice(0, 5)}
|
||||
renderRow={(insight: InsightModel, index) => <InsightRow key={index} insight={insight} />}
|
||||
/>
|
||||
)
|
||||
|
@ -524,10 +524,10 @@ export const featureFlagLogic = kea<featureFlagLogicType>([
|
||||
}
|
||||
},
|
||||
},
|
||||
recentInsights: [
|
||||
relatedInsights: [
|
||||
[] as InsightModel[],
|
||||
{
|
||||
loadRecentInsights: async () => {
|
||||
loadRelatedInsights: async () => {
|
||||
if (props.id && props.id !== 'new' && values.featureFlag.key) {
|
||||
const response = await api.get(
|
||||
`api/projects/${values.currentTeamId}/insights/?feature_flag=${values.featureFlag.key}&order=-created_at`
|
||||
@ -661,7 +661,7 @@ export const featureFlagLogic = kea<featureFlagLogicType>([
|
||||
}
|
||||
},
|
||||
loadFeatureFlagSuccess: async () => {
|
||||
actions.loadRecentInsights()
|
||||
actions.loadRelatedInsights()
|
||||
actions.loadAllInsightsForFlag()
|
||||
},
|
||||
loadInsightAtIndex: async ({ index, filters }) => {
|
||||
@ -1010,7 +1010,7 @@ export const featureFlagLogic = kea<featureFlagLogicType>([
|
||||
if (foundFlag) {
|
||||
const formatPayloads = variantKeyToIndexFeatureFlagPayloads(foundFlag)
|
||||
actions.setFeatureFlag(formatPayloads)
|
||||
actions.loadRecentInsights()
|
||||
actions.loadRelatedInsights()
|
||||
actions.loadAllInsightsForFlag()
|
||||
} else if (props.id !== 'new') {
|
||||
actions.loadFeatureFlag()
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Meta } from '@storybook/react'
|
||||
import { Meta, StoryFn } from '@storybook/react'
|
||||
import { useEffect } from 'react'
|
||||
import { mswDecorator } from '~/mocks/browser'
|
||||
import { router } from 'kea-router'
|
||||
@ -347,63 +347,68 @@ const meta: Meta = {
|
||||
],
|
||||
}
|
||||
export default meta
|
||||
export function NotebooksList(): JSX.Element {
|
||||
export const NotebooksList: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.notebooks())
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function Headings(): JSX.Element {
|
||||
export const Headings: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.notebook('headings'))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function TextFormats(): JSX.Element {
|
||||
export const TextFormats: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.notebook('text-formats'))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function NumberedList(): JSX.Element {
|
||||
export const NumberedList: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.notebook('numbered-list'))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function BulletList(): JSX.Element {
|
||||
export const BulletList: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.notebook('bullet-list'))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function RecordingsPlaylist(): JSX.Element {
|
||||
export const RecordingsPlaylist: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.notebook('recordings-playlist'))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
RecordingsPlaylist.parameters = {
|
||||
testOptions: {
|
||||
waitForSelector: '.NotebookNode__content', // All stories with widget-style nodes needs this
|
||||
},
|
||||
}
|
||||
|
||||
export function TextOnlyNotebook(): JSX.Element {
|
||||
export const TextOnlyNotebook: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.notebook('12345'))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function EmptyNotebook(): JSX.Element {
|
||||
export const EmptyNotebook: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.notebook('empty'))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function NotebookNotFound(): JSX.Element {
|
||||
export const NotebookNotFound: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.notebook('abcde'))
|
||||
}, [])
|
||||
|
@ -213,12 +213,18 @@ export const notebookLogic = kea<notebookLogicType>([
|
||||
} else if (props.shortId.startsWith('template-')) {
|
||||
response =
|
||||
values.notebookTemplates.find((template) => template.short_id === props.shortId) || null
|
||||
if (!response) {
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
response = await api.notebooks.get(props.shortId)
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
throw new Error('Notebook not found')
|
||||
try {
|
||||
response = await api.notebooks.get(props.shortId)
|
||||
} catch (e: any) {
|
||||
if (e.status === 404) {
|
||||
return null
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
const notebook = migrate(response)
|
||||
|
@ -52,7 +52,7 @@ export const notebookSelectButtonLogic = kea<notebookSelectButtonLogicType>([
|
||||
[] as NotebookListItemType[],
|
||||
{
|
||||
loadAllNotebooks: async (_, breakpoint) => {
|
||||
breakpoint(100)
|
||||
await breakpoint(100)
|
||||
const response = await api.notebooks.list(undefined, undefined, values.searchQuery ?? undefined)
|
||||
// TODO for simplicity we'll assume the results will fit into one page
|
||||
return response.results
|
||||
@ -63,7 +63,7 @@ export const notebookSelectButtonLogic = kea<notebookSelectButtonLogicType>([
|
||||
[] as NotebookListItemType[],
|
||||
{
|
||||
loadNotebooksContainingResource: async (_, breakpoint) => {
|
||||
breakpoint(100)
|
||||
await breakpoint(100)
|
||||
if (!props.resource) {
|
||||
return []
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { mswDecorator } from '~/mocks/browser'
|
||||
import { App } from 'scenes/App'
|
||||
import { router } from 'kea-router'
|
||||
import { urls } from 'scenes/urls'
|
||||
import { EMPTY_PAGINATED_RESPONSE } from '~/mocks/handlers'
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'Scenes-App/Project Homepage',
|
||||
@ -13,6 +14,8 @@ const meta: Meta = {
|
||||
'/api/projects/:team_id/dashboards/': require('../dashboard/__mocks__/dashboards.json'),
|
||||
'/api/projects/:team_id/dashboards/1/': require('../dashboard/__mocks__/dashboard1.json'),
|
||||
'/api/projects/:team_id/dashboards/1/collaborators/': [],
|
||||
'/api/projects/:team_id/session_recordings/': EMPTY_PAGINATED_RESPONSE,
|
||||
'/api/projects/:team_id/insights/my_last_viewed/': [],
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
@ -311,7 +311,7 @@ export const sessionRecordingDataLogic = kea<sessionRecordingDataLogicType>([
|
||||
if (!values.sessionPlayerMetaData) {
|
||||
return null
|
||||
}
|
||||
breakpoint(100)
|
||||
await breakpoint(100)
|
||||
await api.recordings.persist(props.sessionRecordingId)
|
||||
|
||||
return {
|
||||
|
@ -3,8 +3,15 @@ import { App } from 'scenes/App'
|
||||
import { urls } from 'scenes/urls'
|
||||
import { mswDecorator } from '~/mocks/browser'
|
||||
import { toPaginatedResponse } from '~/mocks/handlers'
|
||||
import { PropertyFilterType, PropertyOperator, Survey, SurveyQuestionType, SurveyType } from '~/types'
|
||||
import { Meta } from '@storybook/react'
|
||||
import {
|
||||
FeatureFlagBasicType,
|
||||
PropertyFilterType,
|
||||
PropertyOperator,
|
||||
Survey,
|
||||
SurveyQuestionType,
|
||||
SurveyType,
|
||||
} from '~/types'
|
||||
import { Meta, StoryFn } from '@storybook/react'
|
||||
import { router } from 'kea-router'
|
||||
import { SurveyEditSection, surveyLogic } from './surveyLogic'
|
||||
|
||||
@ -103,26 +110,6 @@ const MOCK_SURVEY_WITH_RELEASE_CONS: Survey = {
|
||||
archived: false,
|
||||
}
|
||||
|
||||
// const MOCK_SURVEY_DISMISSED = {
|
||||
// "clickhouse": "SELECT count() AS `survey dismissed` FROM events WHERE and(equals(events.team_id, 1), equals(events.event, %(hogql_val_0)s), equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, %(hogql_val_1)s), ''), 'null'), '^\"|\"$', ''), %(hogql_val_2)s)) LIMIT 100 SETTINGS readonly=2, max_execution_time=60",
|
||||
// "columns": [
|
||||
// "survey dismissed"
|
||||
// ],
|
||||
// "hogql": "SELECT count() AS `survey dismissed` FROM events WHERE and(equals(event, 'survey dismissed'), equals(properties.$survey_id, '0188e637-3b72-0000-f407-07a338652af9')) LIMIT 100",
|
||||
// "query": "select count() as 'survey dismissed' from events where event == 'survey dismissed' and properties.$survey_id == '0188e637-3b72-0000-f407-07a338652af9'",
|
||||
// "results": [
|
||||
// [
|
||||
// 0
|
||||
// ]
|
||||
// ],
|
||||
// "types": [
|
||||
// [
|
||||
// "survey dismissed",
|
||||
// "UInt64"
|
||||
// ]
|
||||
// ]
|
||||
// }
|
||||
|
||||
const MOCK_SURVEY_SHOWN = {
|
||||
clickhouse:
|
||||
"SELECT count() AS `survey shown` FROM events WHERE and(equals(events.team_id, 1), equals(events.event, %(hogql_val_0)s), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, %(hogql_val_1)s), ''), 'null'), '^\"|\"$', ''), %(hogql_val_2)s), 0)) LIMIT 100 SETTINGS readonly=2, max_execution_time=60",
|
||||
@ -170,34 +157,42 @@ const meta: Meta = {
|
||||
'/api/projects/:team_id/surveys/0187c279-bcae-0000-34f5-4f121921f005/': MOCK_BASIC_SURVEY,
|
||||
'/api/projects/:team_id/surveys/0187c279-bcae-0000-34f5-4f121921f006/': MOCK_SURVEY_WITH_RELEASE_CONS,
|
||||
'/api/projects/:team_id/surveys/responses_count/': MOCK_RESPONSES_COUNT,
|
||||
[`/api/projects/:team_id/feature_flags/${
|
||||
(MOCK_SURVEY_WITH_RELEASE_CONS.linked_flag as FeatureFlagBasicType).id
|
||||
}`]: toPaginatedResponse([MOCK_SURVEY_WITH_RELEASE_CONS.linked_flag]),
|
||||
[`/api/projects/:team_id/feature_flags/${
|
||||
(MOCK_SURVEY_WITH_RELEASE_CONS.targeting_flag as FeatureFlagBasicType).id
|
||||
}`]: toPaginatedResponse([MOCK_SURVEY_WITH_RELEASE_CONS.targeting_flag]),
|
||||
},
|
||||
post: {
|
||||
'/api/projects/:team_id/query/': (req) => {
|
||||
if ((req.body as any).kind == 'EventsQuery') {
|
||||
return MOCK_SURVEY_RESULTS
|
||||
'/api/projects/:team_id/query/': async (req, res, ctx) => {
|
||||
const body = await req.json()
|
||||
if (body.kind == 'EventsQuery') {
|
||||
return res(ctx.json(MOCK_SURVEY_RESULTS))
|
||||
} else {
|
||||
return res(ctx.json(MOCK_SURVEY_SHOWN))
|
||||
}
|
||||
return MOCK_SURVEY_SHOWN
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
}
|
||||
export default meta
|
||||
export function SurveysList(): JSX.Element {
|
||||
export const SurveysList: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.surveys())
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function NewSurvey(): JSX.Element {
|
||||
export const NewSurvey: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.survey('new'))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function NewSurveyCustomisationSection(): JSX.Element {
|
||||
export const NewSurveyCustomisationSection: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.survey('new'))
|
||||
surveyLogic({ id: 'new' }).mount()
|
||||
@ -206,7 +201,7 @@ export function NewSurveyCustomisationSection(): JSX.Element {
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function NewSurveyPresentationSection(): JSX.Element {
|
||||
export const NewSurveyPresentationSection: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.survey('new'))
|
||||
surveyLogic({ id: 'new' }).mount()
|
||||
@ -215,7 +210,7 @@ export function NewSurveyPresentationSection(): JSX.Element {
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function NewSurveyTargetingSection(): JSX.Element {
|
||||
export const NewSurveyTargetingSection: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.survey('new'))
|
||||
surveyLogic({ id: 'new' }).mount()
|
||||
@ -233,7 +228,7 @@ export function NewSurveyTargetingSection(): JSX.Element {
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function NewSurveyAppearanceSection(): JSX.Element {
|
||||
export const NewSurveyAppearanceSection: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.survey('new'))
|
||||
surveyLogic({ id: 'new' }).mount()
|
||||
@ -242,21 +237,26 @@ export function NewSurveyAppearanceSection(): JSX.Element {
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function SurveyView(): JSX.Element {
|
||||
export const SurveyView: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.survey(MOCK_SURVEY_WITH_RELEASE_CONS.id))
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
SurveyView.parameters = {
|
||||
testOptions: {
|
||||
skip: true, // FIXME: Fix the mocked data so that survey results can actually load
|
||||
},
|
||||
}
|
||||
|
||||
export function SurveyTemplates(): JSX.Element {
|
||||
export const SurveyTemplates: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.surveyTemplates())
|
||||
}, [])
|
||||
return <App />
|
||||
}
|
||||
|
||||
export function SurveyNotFound(): JSX.Element {
|
||||
export const SurveyNotFound: StoryFn = () => {
|
||||
useEffect(() => {
|
||||
router.actions.push(urls.survey('1234566789'))
|
||||
}, [])
|
||||
|
@ -134,7 +134,10 @@ export const surveyLogic = kea<surveyLogicType>([
|
||||
actions.reportSurveyViewed(survey)
|
||||
return survey
|
||||
} catch (error: any) {
|
||||
actions.setSurveyMissing()
|
||||
if (error.status === 404) {
|
||||
actions.setSurveyMissing()
|
||||
return { ...NEW_SURVEY }
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
"test:visual-regression:legacy:docker": "STORYBOOK_URL=http://host.docker.internal:6006 playwright test -u",
|
||||
"test:visual-regression:legacy:ci:update": "playwright test -u",
|
||||
"test:visual-regression:legacy:ci:verify": "playwright test",
|
||||
"test:visual-regression:stories": "docker compose -f docker-compose.playwright.yml run --rm -it --build playwright pnpm test:visual-regression:stories:docker",
|
||||
"test:visual-regression:stories": "rm -rf frontend/__snapshots__/__failures__/ && docker compose -f docker-compose.playwright.yml run --rm -it --build playwright pnpm test:visual-regression:stories:docker",
|
||||
"test:visual-regression:stories:docker": "NODE_OPTIONS=--max-old-space-size=6144 test-storybook -u --no-index-json --browsers chromium webkit --url http://host.docker.internal:6006",
|
||||
"test:visual-regression:stories:ci:update": "test-storybook -u --no-index-json --maxWorkers=2",
|
||||
"test:visual-regression:stories:ci:verify": "test-storybook --ci --no-index-json --maxWorkers=2",
|
||||
@ -303,8 +303,7 @@
|
||||
"!(posthog/hogql/grammar/*)*.{py,pyi}": [
|
||||
"ruff format",
|
||||
"ruff check"
|
||||
],
|
||||
"!(HogQL*)*.{c,cpp,h,hpp}": "clang-format -i"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"development": [
|
||||
|
@ -602,7 +602,7 @@ devDependencies:
|
||||
version: 7.0.1(monaco-editor@0.39.0)(webpack@5.88.2)
|
||||
msw:
|
||||
specifier: ^0.49.0
|
||||
version: 0.49.3(typescript@4.9.5)
|
||||
version: 0.49.0(typescript@4.9.5)
|
||||
path-browserify:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
@ -3254,16 +3254,16 @@ packages:
|
||||
set-cookie-parser: 2.5.1
|
||||
dev: true
|
||||
|
||||
/@mswjs/interceptors@0.17.6:
|
||||
resolution: {integrity: sha512-201pBIWehTURb6q8Gheu4Zhvd3Ox1U4BJq5KiOQsYzkWyfiOG4pwcz5hPZIEryztgrf8/sdwABpvY757xMmfrQ==}
|
||||
/@mswjs/interceptors@0.17.10:
|
||||
resolution: {integrity: sha512-N8x7eSLGcmUFNWZRxT1vsHvypzIRgQYdG0rJey/rZCy6zT/30qDt8Joj7FxzGNLSwXbeZqJOMqDurp7ra4hgbw==}
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
'@open-draft/until': 1.0.3
|
||||
'@types/debug': 4.1.7
|
||||
'@xmldom/xmldom': 0.8.6
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
headers-polyfill: 3.1.2
|
||||
outvariant: 1.3.0
|
||||
headers-polyfill: 3.2.5
|
||||
outvariant: 1.4.0
|
||||
strict-event-emitter: 0.2.8
|
||||
web-encoding: 1.1.5
|
||||
transitivePeerDependencies:
|
||||
@ -5071,7 +5071,7 @@ packages:
|
||||
flat-cache: 3.0.4
|
||||
micromatch: 4.0.5
|
||||
react-docgen-typescript: 2.2.2(typescript@4.9.5)
|
||||
tslib: 2.4.1
|
||||
tslib: 2.6.2
|
||||
typescript: 4.9.5
|
||||
webpack: 5.88.2(@swc/core@1.3.93)(esbuild@0.14.54)(webpack-cli@5.1.4)
|
||||
transitivePeerDependencies:
|
||||
@ -5909,7 +5909,7 @@ packages:
|
||||
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
|
||||
dependencies:
|
||||
'@types/connect': 3.4.38
|
||||
'@types/node': 18.11.9
|
||||
'@types/node': 18.18.4
|
||||
dev: true
|
||||
|
||||
/@types/chart.js@2.9.37:
|
||||
@ -5937,7 +5937,7 @@ packages:
|
||||
/@types/connect@3.4.38:
|
||||
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
|
||||
dependencies:
|
||||
'@types/node': 18.11.9
|
||||
'@types/node': 18.18.4
|
||||
dev: true
|
||||
|
||||
/@types/cookie@0.4.1:
|
||||
@ -6215,7 +6215,7 @@ packages:
|
||||
/@types/express-serve-static-core@4.17.41:
|
||||
resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==}
|
||||
dependencies:
|
||||
'@types/node': 18.11.9
|
||||
'@types/node': 18.18.4
|
||||
'@types/qs': 6.9.10
|
||||
'@types/range-parser': 1.2.7
|
||||
'@types/send': 0.17.4
|
||||
@ -6545,7 +6545,7 @@ packages:
|
||||
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
|
||||
dependencies:
|
||||
'@types/mime': 1.3.5
|
||||
'@types/node': 18.11.9
|
||||
'@types/node': 18.18.4
|
||||
dev: true
|
||||
|
||||
/@types/serve-static@1.15.4:
|
||||
@ -6561,7 +6561,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/http-errors': 2.0.4
|
||||
'@types/mime': 3.0.4
|
||||
'@types/node': 18.11.9
|
||||
'@types/node': 18.18.4
|
||||
dev: true
|
||||
|
||||
/@types/set-cookie-parser@2.4.2:
|
||||
@ -11389,8 +11389,8 @@ packages:
|
||||
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
|
||||
dev: true
|
||||
|
||||
/graphql@16.6.0:
|
||||
resolution: {integrity: sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==}
|
||||
/graphql@16.8.1:
|
||||
resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==}
|
||||
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
|
||||
dev: true
|
||||
|
||||
@ -11486,8 +11486,8 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/headers-polyfill@3.1.2:
|
||||
resolution: {integrity: sha512-tWCK4biJ6hcLqTviLXVR9DTRfYGQMXEIUj3gwJ2rZ5wO/at3XtkI4g8mCvFdUF9l1KMBNCfmNAdnahm1cgavQA==}
|
||||
/headers-polyfill@3.2.5:
|
||||
resolution: {integrity: sha512-tUCGvt191vNSQgttSyJoibR+VO+I6+iCHIUdhzEMJKE+EAL8BwCN7fUOZlY4ofOelNHsK+gEjxB/B+9N3EWtdA==}
|
||||
dev: true
|
||||
|
||||
/helpertypes@0.0.19:
|
||||
@ -12110,8 +12110,8 @@ packages:
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: true
|
||||
|
||||
/is-node-process@1.0.1:
|
||||
resolution: {integrity: sha512-5IcdXuf++TTNt3oGl9EBdkvndXA8gmc4bz/Y+mdEpWh3Mcn/+kOw6hI7LD5CocqJWMzeb0I0ClndRVNdEPuJXQ==}
|
||||
/is-node-process@1.2.0:
|
||||
resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==}
|
||||
dev: true
|
||||
|
||||
/is-number-object@1.0.7:
|
||||
@ -14380,8 +14380,8 @@ packages:
|
||||
/ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
/msw@0.49.3(typescript@4.9.5):
|
||||
resolution: {integrity: sha512-kRCbDNbNnRq5LC1H/NUceZlrPAvSrMH6Or0mirIuH69NY84xwDruPn/hkXTovIK1KwDwbk+ZdoSyJlpiekLxEA==}
|
||||
/msw@0.49.0(typescript@4.9.5):
|
||||
resolution: {integrity: sha512-xX5RMSMjN58j8G/V26Uaf5LP464VltuWyd66TQimLueVYfG47RKydGsd4JW165Jb/gjoaQxh5Tdvv31wdZAOlA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
@ -14392,22 +14392,22 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@mswjs/cookies': 0.2.2
|
||||
'@mswjs/interceptors': 0.17.6
|
||||
'@mswjs/interceptors': 0.17.10
|
||||
'@open-draft/until': 1.0.3
|
||||
'@types/cookie': 0.4.1
|
||||
'@types/js-levenshtein': 1.1.1
|
||||
chalk: 4.1.1
|
||||
chokidar: 3.5.3
|
||||
cookie: 0.4.2
|
||||
graphql: 16.6.0
|
||||
headers-polyfill: 3.1.2
|
||||
graphql: 16.8.1
|
||||
headers-polyfill: 3.2.5
|
||||
inquirer: 8.2.5
|
||||
is-node-process: 1.0.1
|
||||
is-node-process: 1.2.0
|
||||
js-levenshtein: 1.1.6
|
||||
node-fetch: 2.6.7
|
||||
outvariant: 1.3.0
|
||||
outvariant: 1.4.0
|
||||
path-to-regexp: 6.2.1
|
||||
strict-event-emitter: 0.4.6
|
||||
strict-event-emitter: 0.2.8
|
||||
type-fest: 2.19.0
|
||||
typescript: 4.9.5
|
||||
yargs: 17.6.2
|
||||
@ -14801,8 +14801,8 @@ packages:
|
||||
resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
|
||||
dev: true
|
||||
|
||||
/outvariant@1.3.0:
|
||||
resolution: {integrity: sha512-yeWM9k6UPfG/nzxdaPlJkB2p08hCg4xP6Lx99F+vP8YF7xyZVfTmJjrrNalkmzudD4WFvNLVudQikqUmF8zhVQ==}
|
||||
/outvariant@1.4.0:
|
||||
resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==}
|
||||
dev: true
|
||||
|
||||
/p-limit@2.3.0:
|
||||
@ -16694,7 +16694,7 @@ packages:
|
||||
react: 18.2.0
|
||||
react-remove-scroll-bar: 2.3.4(@types/react@17.0.52)(react@18.2.0)
|
||||
react-style-singleton: 2.2.1(@types/react@17.0.52)(react@18.2.0)
|
||||
tslib: 2.4.1
|
||||
tslib: 2.6.2
|
||||
use-callback-ref: 1.3.0(@types/react@17.0.52)(react@18.2.0)
|
||||
use-sidecar: 1.1.2(@types/react@17.0.52)(react@18.2.0)
|
||||
dev: true
|
||||
@ -17758,10 +17758,6 @@ packages:
|
||||
events: 3.3.0
|
||||
dev: true
|
||||
|
||||
/strict-event-emitter@0.4.6:
|
||||
resolution: {integrity: sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg==}
|
||||
dev: true
|
||||
|
||||
/string-argv@0.3.1:
|
||||
resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
|
||||
engines: {node: '>=0.6.19'}
|
||||
|
27
test-runner-jest-environment.js
Normal file
27
test-runner-jest-environment.js
Normal file
@ -0,0 +1,27 @@
|
||||
const { setupPage } = require('@storybook/test-runner')
|
||||
const PlaywrightEnvironment = require('jest-playwright-preset/lib/PlaywrightEnvironment').default
|
||||
|
||||
class CustomEnvironment extends PlaywrightEnvironment {
|
||||
async setup() {
|
||||
await super.setup()
|
||||
await setupPage(this.global.page, this.global.context)
|
||||
}
|
||||
|
||||
async teardown() {
|
||||
await super.teardown()
|
||||
}
|
||||
|
||||
async handleTestEvent(event) {
|
||||
if (event.name === 'test_done' && event.test.errors.length > 0) {
|
||||
// Take screenshots on test failures - these become Actions artifacts
|
||||
const parentName = event.test.parent.parent.name.replace(/\W/g, '-').toLowerCase()
|
||||
const specName = event.test.parent.name.replace(/\W/g, '-').toLowerCase()
|
||||
await this.global.page.screenshot({
|
||||
path: `frontend/__snapshots__/__failures__/${parentName}--${specName}.png`,
|
||||
})
|
||||
}
|
||||
await super.handleTestEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomEnvironment
|
@ -1,5 +1,8 @@
|
||||
const { getJestConfig } = require('@storybook/test-runner')
|
||||
|
||||
/**
|
||||
* @type {import('@jest/types').Config.InitialOptions}
|
||||
*/
|
||||
module.exports = {
|
||||
// The default configuration comes from @storybook/test-runner
|
||||
...getJestConfig(),
|
||||
@ -7,7 +10,7 @@ module.exports = {
|
||||
* @see https://jestjs.io/docs/configuration
|
||||
*/
|
||||
forceExit: true,
|
||||
// Remove obsolete snapshots in CI
|
||||
// See https://github.com/americanexpress/jest-image-snapshot#removing-outdated-snapshots
|
||||
// For jest-image-snapshot, see https://github.com/americanexpress/jest-image-snapshot#removing-outdated-snapshots
|
||||
reporters: ['default', 'jest-image-snapshot/src/outdated-snapshot-reporter.js'],
|
||||
testEnvironment: './test-runner-jest-environment.js',
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user