From a43b99368e9a98934d3c756a9cd56f12aefdc590 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 30 Oct 2024 17:25:30 -0700 Subject: [PATCH] chore: support aria snapshots in tsx (#33369) --- .../bundles/babel/src/babelBundleImpl.ts | 14 +++++-- packages/playwright/src/runner/rebase.ts | 4 +- .../playwright/src/transform/babelBundle.ts | 7 ++-- .../playwright/src/transform/transform.ts | 3 +- .../update-aria-snapshot.spec.ts | 41 ++++++++++++++++++- 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/packages/playwright/bundles/babel/src/babelBundleImpl.ts b/packages/playwright/bundles/babel/src/babelBundleImpl.ts index 5e09be2a0c..78c3c0403e 100644 --- a/packages/playwright/bundles/babel/src/babelBundleImpl.ts +++ b/packages/playwright/bundles/babel/src/babelBundleImpl.ts @@ -23,7 +23,6 @@ import * as babel from '@babel/core'; export { codeFrameColumns } from '@babel/code-frame'; export { declare } from '@babel/helper-plugin-utils'; export { types } from '@babel/core'; -export { parse } from '@babel/parser'; import traverseFunction from '@babel/traverse'; export const traverse = traverseFunction; @@ -114,16 +113,25 @@ function babelTransformOptions(isTypeScript: boolean, isModule: boolean, plugins let isTransforming = false; -export function babelTransform(code: string, filename: string, isTypeScript: boolean, isModule: boolean, pluginsPrologue: [string, any?][], pluginsEpilogue: [string, any?][]): BabelFileResult { +function isTypeScript(filename: string) { + return filename.endsWith('.ts') || filename.endsWith('.tsx') || filename.endsWith('.mts') || filename.endsWith('.cts'); +} + +export function babelTransform(code: string, filename: string, isModule: boolean, pluginsPrologue: [string, any?][], pluginsEpilogue: [string, any?][]): BabelFileResult { if (isTransforming) return {}; // Prevent reentry while requiring plugins lazily. isTransforming = true; try { - const options = babelTransformOptions(isTypeScript, isModule, pluginsPrologue, pluginsEpilogue); + const options = babelTransformOptions(isTypeScript(filename), isModule, pluginsPrologue, pluginsEpilogue); return babel.transform(code, { filename, ...options })!; } finally { isTransforming = false; } } + +export function babelParse(code: string, filename: string, isModule: boolean): babel.ParseResult { + const options = babelTransformOptions(isTypeScript(filename), isModule, [], []); + return babel.parse(code, { filename, ...options })!; +} diff --git a/packages/playwright/src/runner/rebase.ts b/packages/playwright/src/runner/rebase.ts index 17717e977e..d0d7a85c86 100644 --- a/packages/playwright/src/runner/rebase.ts +++ b/packages/playwright/src/runner/rebase.ts @@ -17,7 +17,7 @@ import path from 'path'; import fs from 'fs'; import type { T } from '../transform/babelBundle'; -import { types, traverse, parse } from '../transform/babelBundle'; +import { types, traverse, babelParse } from '../transform/babelBundle'; import { MultiMap } from 'playwright-core/lib/utils'; import { generateUnifiedDiff } from 'playwright-core/lib/utils'; import type { FullConfigInternal } from '../common/config'; @@ -53,7 +53,7 @@ export async function applySuggestedRebaselines(config: FullConfigInternal) { const source = await fs.promises.readFile(fileName, 'utf8'); const lines = source.split('\n'); const replacements = suggestedRebaselines.get(fileName); - const fileNode = parse(source, { sourceType: 'module' }); + const fileNode = babelParse(source, fileName, true); const ranges: { start: number, end: number, oldText: string, newText: string }[] = []; traverse(fileNode, { diff --git a/packages/playwright/src/transform/babelBundle.ts b/packages/playwright/src/transform/babelBundle.ts index 2806a05aec..d2f8b5919a 100644 --- a/packages/playwright/src/transform/babelBundle.ts +++ b/packages/playwright/src/transform/babelBundle.ts @@ -14,14 +14,15 @@ * limitations under the License. */ -import type { BabelFileResult } from '../../bundles/babel/node_modules/@types/babel__core'; +import type { BabelFileResult, ParseResult } from '../../bundles/babel/node_modules/@types/babel__core'; export const codeFrameColumns: typeof import('../../bundles/babel/node_modules/@types/babel__code-frame').codeFrameColumns = require('./babelBundleImpl').codeFrameColumns; export const declare: typeof import('../../bundles/babel/node_modules/@types/babel__helper-plugin-utils').declare = require('./babelBundleImpl').declare; export const types: typeof import('../../bundles/babel/node_modules/@types/babel__core').types = require('./babelBundleImpl').types; -export const parse: typeof import('../../bundles/babel/node_modules/@babel/parser/typings/babel-parser').parse = require('./babelBundleImpl').parse; export const traverse: typeof import('../../bundles/babel/node_modules/@types/babel__traverse').default = require('./babelBundleImpl').traverse; export type BabelPlugin = [string, any?]; -export type BabelTransformFunction = (code: string, filename: string, isTypeScript: boolean, isModule: boolean, pluginsPrefix: BabelPlugin[], pluginsSuffix: BabelPlugin[]) => BabelFileResult; +export type BabelTransformFunction = (code: string, filename: string, isModule: boolean, pluginsPrefix: BabelPlugin[], pluginsSuffix: BabelPlugin[]) => BabelFileResult; export const babelTransform: BabelTransformFunction = require('./babelBundleImpl').babelTransform; +export type BabelParseFunction = (code: string, filename: string, isModule: boolean) => ParseResult; +export const babelParse: BabelParseFunction = require('./babelBundleImpl').babelParse; export type { NodePath, types as T, PluginObj } from '../../bundles/babel/node_modules/@types/babel__core'; export type { BabelAPI } from '../../bundles/babel/node_modules/@types/babel__helper-plugin-utils'; diff --git a/packages/playwright/src/transform/transform.ts b/packages/playwright/src/transform/transform.ts index f70f385b5b..549d83a168 100644 --- a/packages/playwright/src/transform/transform.ts +++ b/packages/playwright/src/transform/transform.ts @@ -215,7 +215,6 @@ export function setTransformData(pluginName: string, value: any) { } export function transformHook(originalCode: string, filename: string, moduleUrl?: string): { code: string, serializedCache?: any } { - const isTypeScript = filename.endsWith('.ts') || filename.endsWith('.tsx') || filename.endsWith('.mts') || filename.endsWith('.cts'); const hasPreprocessor = process.env.PW_TEST_SOURCE_TRANSFORM && process.env.PW_TEST_SOURCE_TRANSFORM_SCOPE && @@ -233,7 +232,7 @@ export function transformHook(originalCode: string, filename: string, moduleUrl? const { babelTransform }: { babelTransform: BabelTransformFunction } = require('./babelBundle'); transformData = new Map(); - const { code, map } = babelTransform(originalCode, filename, isTypeScript, !!moduleUrl, pluginsPrologue, pluginsEpilogue); + const { code, map } = babelTransform(originalCode, filename, !!moduleUrl, pluginsPrologue, pluginsEpilogue); if (!code) return { code: '', serializedCache }; const added = addToCache!(code, map, transformData); diff --git a/tests/playwright-test/update-aria-snapshot.spec.ts b/tests/playwright-test/update-aria-snapshot.spec.ts index 203fda16a5..9f334f8355 100644 --- a/tests/playwright-test/update-aria-snapshot.spec.ts +++ b/tests/playwright-test/update-aria-snapshot.spec.ts @@ -15,7 +15,7 @@ */ import * as fs from 'fs'; -import { test, expect } from './playwright-test-fixtures'; +import { test, expect, playwrightCtConfigText } from './playwright-test-fixtures'; test.describe.configure({ mode: 'parallel' }); @@ -163,3 +163,42 @@ test('should generate baseline with special characters', async ({ runInlineTest `); }); + +test('should update missing snapshots in tsx', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': playwrightCtConfigText, + 'playwright/index.html': ``, + 'playwright/index.ts': ``, + + 'src/button.tsx': ` + export const Button = () => ; + `, + + 'src/button.test.tsx': ` + import { test, expect } from '@playwright/experimental-ct-react'; + import { Button } from './button.tsx'; + + test('pass', async ({ mount }) => { + const component = await mount(); + await expect(component).toMatchAriaSnapshot(\`\`); + }); + `, + }); + + expect(result.exitCode).toBe(0); + const patchPath = testInfo.outputPath('test-results/rebaselines.patch'); + const data = fs.readFileSync(patchPath, 'utf-8'); + expect(data).toBe(`--- a/src/button.test.tsx ++++ b/src/button.test.tsx +@@ -4,6 +4,8 @@ + + test('pass', async ({ mount }) => { + const component = await mount(); +- await expect(component).toMatchAriaSnapshot(\`\`); ++ await expect(component).toMatchAriaSnapshot(\` ++ - button \"Button\" ++ \`); + }); + +`); +});