diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index 1555a7a0b7..89a16f1a10 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -17,6 +17,25 @@ export default defineConfig({ }); ``` +## property: TestConfig.build +* since: v1.35 +- type: ?<[Object]> + - `external` ?<[Array]<[string]>> Paths to exclude from the transpilation expressed as glob patterns. Typically heavy JS bundles your tests reference. + +Transpiler configuration. + +**Usage** + +```js title="playwright.config.ts" +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + build: { + external: '**/*bundle.js', + }, +}); +``` + ## property: TestConfig.expect * since: v1.10 - type: ?<[Object]> diff --git a/packages/playwright-ct-core/index.d.ts b/packages/playwright-ct-core/index.d.ts new file mode 100644 index 0000000000..e7717c9b68 --- /dev/null +++ b/packages/playwright-ct-core/index.d.ts @@ -0,0 +1,66 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { + TestType, + PlaywrightTestArgs, + PlaywrightTestConfig as BasePlaywrightTestConfig, + PlaywrightTestOptions, + PlaywrightWorkerArgs, + PlaywrightWorkerOptions, + Locator, +} from '@playwright/test'; +import type { JsonObject } from '@playwright/experimental-ct-core/types/component'; +import type { InlineConfig } from 'vite'; + +export type PlaywrightTestConfig = Omit, 'use'> & { + use?: BasePlaywrightTestConfig['use'] & { + ctPort?: number; + ctTemplateDir?: string; + ctCacheDir?: string; + ctViteConfig?: InlineConfig | (() => Promise); + }; +}; + +export interface MountOptions { + hooksConfig?: HooksConfig; +} + +interface MountResult extends Locator { + unmount(): Promise; + update(component: JSX.Element): Promise; +} + +export interface ComponentFixtures { + mount( + component: JSX.Element, + options?: MountOptions + ): Promise; +} + +export const test: TestType< + PlaywrightTestArgs & PlaywrightTestOptions & ComponentFixtures, + PlaywrightWorkerArgs & PlaywrightWorkerOptions +>; + +/** + * Defines Playwright config + */ +export function defineConfig(config: PlaywrightTestConfig): PlaywrightTestConfig; +export function defineConfig(config: PlaywrightTestConfig): PlaywrightTestConfig; +export function defineConfig(config: PlaywrightTestConfig): PlaywrightTestConfig; + +export { expect, devices } from '@playwright/test'; diff --git a/packages/playwright-ct-core/index.js b/packages/playwright-ct-core/index.js new file mode 100644 index 0000000000..3cccb3d65a --- /dev/null +++ b/packages/playwright-ct-core/index.js @@ -0,0 +1,32 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/test'); +const { fixtures } = require('./lib/mount'); + +const defineConfig = config => originalDefineConfig({ + ...config, + build: { + ...config.build, + babelPlugins: [ + [require.resolve('./lib/tsxTransform')] + ], + } +}); + +const test = baseTest.extend(fixtures); + +module.exports = { test, expect, devices, defineConfig }; diff --git a/packages/playwright-ct-core/package.json b/packages/playwright-ct-core/package.json index 03a9de892f..0817b4ca0c 100644 --- a/packages/playwright-ct-core/package.json +++ b/packages/playwright-ct-core/package.json @@ -12,6 +12,10 @@ }, "license": "Apache-2.0", "exports": { + ".": { + "types": "./index.d.ts", + "default": "./index.js" + }, "./cli": "./cli.js", "./lib/mount": "./lib/mount.js", "./lib/vitePlugin": "./lib/vitePlugin.js" diff --git a/packages/playwright-ct-core/src/vitePlugin.ts b/packages/playwright-ct-core/src/vitePlugin.ts index 5ed8e2fa4c..13b2150264 100644 --- a/packages/playwright-ct-core/src/vitePlugin.ts +++ b/packages/playwright-ct-core/src/vitePlugin.ts @@ -58,10 +58,6 @@ export function createPlugin( configDir = configDirectory; }, - babelPlugins: async () => [ - [require.resolve('./tsxTransform')] - ], - begin: async (suite: Suite) => { const use = config.projects[0].use as CtConfig; const port = use.ctPort || 3100; diff --git a/packages/playwright-ct-react/index.js b/packages/playwright-ct-react/index.js index 7ec0f6b567..04d69e08f5 100644 --- a/packages/playwright-ct-react/index.js +++ b/packages/playwright-ct-react/index.js @@ -14,8 +14,7 @@ * limitations under the License. */ -const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/test'); -const { fixtures } = require('@playwright/experimental-ct-core/lib/mount'); +const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); const plugin = () => { @@ -26,6 +25,5 @@ const plugin = () => { () => import('@vitejs/plugin-react').then(plugin => plugin.default())); }; const defineConfig = config => originalDefineConfig({ ...config, _plugins: [plugin] }); -const test = baseTest.extend(fixtures); module.exports = { test, expect, devices, defineConfig }; diff --git a/packages/playwright-ct-react17/index.js b/packages/playwright-ct-react17/index.js index 7ec0f6b567..04d69e08f5 100644 --- a/packages/playwright-ct-react17/index.js +++ b/packages/playwright-ct-react17/index.js @@ -14,8 +14,7 @@ * limitations under the License. */ -const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/test'); -const { fixtures } = require('@playwright/experimental-ct-core/lib/mount'); +const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); const plugin = () => { @@ -26,6 +25,5 @@ const plugin = () => { () => import('@vitejs/plugin-react').then(plugin => plugin.default())); }; const defineConfig = config => originalDefineConfig({ ...config, _plugins: [plugin] }); -const test = baseTest.extend(fixtures); module.exports = { test, expect, devices, defineConfig }; diff --git a/packages/playwright-ct-solid/index.js b/packages/playwright-ct-solid/index.js index 6d81c3b0ea..2176a7a95e 100644 --- a/packages/playwright-ct-solid/index.js +++ b/packages/playwright-ct-solid/index.js @@ -14,8 +14,7 @@ * limitations under the License. */ -const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/test'); -const { fixtures } = require('@playwright/experimental-ct-core/lib/mount'); +const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); const plugin = () => { @@ -26,6 +25,5 @@ const plugin = () => { () => import('vite-plugin-solid').then(plugin => plugin.default())); }; const defineConfig = config => originalDefineConfig({ ...config, _plugins: [plugin] }); -const test = baseTest.extend(fixtures); module.exports = { test, expect, devices, defineConfig }; diff --git a/packages/playwright-ct-svelte/index.js b/packages/playwright-ct-svelte/index.js index d315751c05..53b5b14a17 100644 --- a/packages/playwright-ct-svelte/index.js +++ b/packages/playwright-ct-svelte/index.js @@ -14,8 +14,7 @@ * limitations under the License. */ -const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/test'); -const { fixtures } = require('@playwright/experimental-ct-core/lib/mount'); +const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); const plugin = () => { @@ -26,6 +25,5 @@ const plugin = () => { () => import('@sveltejs/vite-plugin-svelte').then(plugin => plugin.svelte())); }; const defineConfig = config => originalDefineConfig({ ...config, _plugins: [plugin] }); -const test = baseTest.extend(fixtures); module.exports = { test, expect, devices, defineConfig }; diff --git a/packages/playwright-ct-vue/index.js b/packages/playwright-ct-vue/index.js index b56283bff8..cb9c629a1f 100644 --- a/packages/playwright-ct-vue/index.js +++ b/packages/playwright-ct-vue/index.js @@ -14,8 +14,7 @@ * limitations under the License. */ -const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/test'); -const { fixtures } = require('@playwright/experimental-ct-core/lib/mount'); +const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); const plugin = () => { @@ -26,6 +25,5 @@ const plugin = () => { () => import('@vitejs/plugin-vue').then(plugin => plugin.default())); } const defineConfig = config => originalDefineConfig({ ...config, _plugins: [plugin] }); -const test = baseTest.extend(fixtures); module.exports = { test, expect, devices, defineConfig }; diff --git a/packages/playwright-ct-vue2/index.js b/packages/playwright-ct-vue2/index.js index 19e0b9d318..9c8c71cb8e 100644 --- a/packages/playwright-ct-vue2/index.js +++ b/packages/playwright-ct-vue2/index.js @@ -14,8 +14,7 @@ * limitations under the License. */ -const { test: baseTest, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/test'); -const { fixtures } = require('@playwright/experimental-ct-core/lib/mount'); +const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); const path = require('path'); const plugin = () => { @@ -26,6 +25,5 @@ const plugin = () => { () => import('@vitejs/plugin-vue2').then(plugin => plugin.default())); }; const defineConfig = config => originalDefineConfig({ ...config, _plugins: [plugin] }); -const test = baseTest.extend(fixtures); module.exports = { test, expect, devices, defineConfig }; diff --git a/packages/playwright-test/src/common/config.ts b/packages/playwright-test/src/common/config.ts index 177bdc36ad..a3c443a277 100644 --- a/packages/playwright-test/src/common/config.ts +++ b/packages/playwright-test/src/common/config.ts @@ -24,6 +24,7 @@ import { getPackageJsonPath, mergeObjects } from '../util'; import type { Matcher } from '../util'; import type { ConfigCLIOverrides } from './ipc'; import type { FullConfig, FullProject } from '../../types/test'; +import { setTransformConfig } from '../transform/transform'; export type FixturesWithLocation = { fixtures: Fixtures; @@ -124,6 +125,10 @@ export class FullConfigInternal { this.projects = projectConfigs.map(p => new FullProjectInternal(configDir, config, this, p, this.configCLIOverrides, throwawayArtifactsPath)); resolveProjectDependencies(this.projects); this._assignUniqueProjectIds(this.projects); + setTransformConfig({ + babelPlugins: (config as any).build?.babelPlugins || [], + external: config.build?.external || [], + }); this.config.projects = this.projects.map(p => p.project); } diff --git a/packages/playwright-test/src/common/configLoader.ts b/packages/playwright-test/src/common/configLoader.ts index 96bde3978f..4e30273f3a 100644 --- a/packages/playwright-test/src/common/configLoader.ts +++ b/packages/playwright-test/src/common/configLoader.ts @@ -18,7 +18,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { isRegExp } from 'playwright-core/lib/utils'; import type { ConfigCLIOverrides, SerializedConfig } from './ipc'; -import { requireOrImport, setBabelPlugins } from '../transform/transform'; +import { requireOrImport } from '../transform/transform'; import type { Config, Project } from '../../types/test'; import { errorWithFile } from '../util'; import { setCurrentConfig } from './globals'; @@ -41,15 +41,12 @@ export class ConfigLoader { } static async deserialize(data: SerializedConfig): Promise { - setBabelPlugins(data.babelTransformPlugins); addToCompilationCache(data.compilationCache); - await initializeEsmLoader(); const loader = new ConfigLoader(data.configCLIOverrides); - - if (data.configFile) - return await loader.loadConfigFile(data.configFile); - return await loader.loadEmptyConfig(data.configDir); + const config = data.configFile ? await loader.loadConfigFile(data.configFile) : await loader.loadEmptyConfig(data.configDir); + await initializeEsmLoader(); + return config; } async loadConfigFile(file: string, ignoreProjectDependencies = false): Promise { diff --git a/packages/playwright-test/src/common/esmLoaderHost.ts b/packages/playwright-test/src/common/esmLoaderHost.ts index 14b40098c0..979a0cce17 100644 --- a/packages/playwright-test/src/common/esmLoaderHost.ts +++ b/packages/playwright-test/src/common/esmLoaderHost.ts @@ -15,7 +15,7 @@ */ import { addToCompilationCache, serializeCompilationCache } from '../transform/compilationCache'; -import { getBabelPlugins } from '../transform/transform'; +import { transformConfig } from '../transform/transform'; import { PortTransport } from '../transform/portTransport'; const port = (globalThis as any).__esmLoaderPort; @@ -47,6 +47,6 @@ export async function incorporateCompilationCache() { export async function initializeEsmLoader() { if (!loaderChannel) return; - await loaderChannel.send('setBabelPlugins', { plugins: getBabelPlugins() }); + await loaderChannel.send('setTransformConfig', { config: transformConfig() }); await loaderChannel.send('addToCompilationCache', { cache: serializeCompilationCache() }); } diff --git a/packages/playwright-test/src/common/ipc.ts b/packages/playwright-test/src/common/ipc.ts index aee8d5b639..c662fe4589 100644 --- a/packages/playwright-test/src/common/ipc.ts +++ b/packages/playwright-test/src/common/ipc.ts @@ -42,7 +42,6 @@ export type SerializedConfig = { configDir: string; configCLIOverrides: ConfigCLIOverrides; compilationCache: any; - babelTransformPlugins: [string, any?][]; }; export type TtyParams = { @@ -126,15 +125,11 @@ export type TeardownErrorsPayload = { export type EnvProducedPayload = [string, string | null][]; export function serializeConfig(config: FullConfigInternal): SerializedConfig { - const babelTransformPlugins: [string, any?][] = []; - for (const plugin of config.plugins) - babelTransformPlugins.push(...plugin.babelPlugins || []); const result: SerializedConfig = { configFile: config.config.configFile, configDir: config.configDir, configCLIOverrides: config.configCLIOverrides, compilationCache: serializeCompilationCache(), - babelTransformPlugins, }; return result; } diff --git a/packages/playwright-test/src/plugins/index.ts b/packages/playwright-test/src/plugins/index.ts index 52cc4a9a45..19640084fb 100644 --- a/packages/playwright-test/src/plugins/index.ts +++ b/packages/playwright-test/src/plugins/index.ts @@ -20,7 +20,6 @@ import type { InternalReporter } from '../reporters/internalReporter'; export interface TestRunnerPlugin { name: string; setup?(config: FullConfig, configDir: string, reporter: InternalReporter): Promise; - babelPlugins?(): Promise<[string, any?][]>; begin?(suite: Suite): Promise; end?(): Promise; teardown?(): Promise; @@ -29,7 +28,6 @@ export interface TestRunnerPlugin { export type TestRunnerPluginRegistration = { factory: TestRunnerPlugin | (() => TestRunnerPlugin | Promise); instance?: TestRunnerPlugin; - babelPlugins?: [string, any?][]; }; export { webServer } from './webServerPlugin'; diff --git a/packages/playwright-test/src/runner/loaderHost.ts b/packages/playwright-test/src/runner/loaderHost.ts index 67aab310e2..70ccf8f0be 100644 --- a/packages/playwright-test/src/runner/loaderHost.ts +++ b/packages/playwright-test/src/runner/loaderHost.ts @@ -22,7 +22,6 @@ import { loadTestFile } from '../common/testLoader'; import type { FullConfigInternal } from '../common/config'; import { PoolBuilder } from '../common/poolBuilder'; import { addToCompilationCache } from '../transform/compilationCache'; -import { setBabelPlugins } from '../transform/transform'; import { incorporateCompilationCache, initializeEsmLoader } from '../common/esmLoaderHost'; export class InProcessLoaderHost { @@ -35,10 +34,6 @@ export class InProcessLoaderHost { } async start() { - const babelTransformPlugins: [string, any?][] = []; - for (const plugin of this._config.plugins) - babelTransformPlugins.push(...plugin.babelPlugins || []); - setBabelPlugins(babelTransformPlugins); await initializeEsmLoader(); } diff --git a/packages/playwright-test/src/runner/tasks.ts b/packages/playwright-test/src/runner/tasks.ts index b4d0f28789..3739dc96f2 100644 --- a/packages/playwright-test/src/runner/tasks.ts +++ b/packages/playwright-test/src/runner/tasks.ts @@ -119,7 +119,6 @@ function createPluginSetupTask(plugin: TestRunnerPluginRegistration): Task plugin.instance?.teardown?.(); }; } diff --git a/packages/playwright-test/src/transform/esmLoader.ts b/packages/playwright-test/src/transform/esmLoader.ts index b8e9618442..1285193daf 100644 --- a/packages/playwright-test/src/transform/esmLoader.ts +++ b/packages/playwright-test/src/transform/esmLoader.ts @@ -16,8 +16,8 @@ import fs from 'fs'; import url from 'url'; -import { addToCompilationCache, belongsToNodeModules, currentFileDepsCollector, serializeCompilationCache, startCollectingFileDeps, stopCollectingFileDeps } from './compilationCache'; -import { transformHook, resolveHook, setBabelPlugins } from './transform'; +import { addToCompilationCache, currentFileDepsCollector, serializeCompilationCache, startCollectingFileDeps, stopCollectingFileDeps } from './compilationCache'; +import { transformHook, resolveHook, setTransformConfig, shouldTransform } from './transform'; import { PortTransport } from './portTransport'; // Node < 18.6: defaultResolve takes 3 arguments. @@ -50,7 +50,7 @@ async function load(moduleUrl: string, context: { format?: string }, defaultLoad const filename = url.fileURLToPath(moduleUrl); // Bail for node_modules. - if (belongsToNodeModules(filename)) + if (!shouldTransform(filename)) return defaultLoad(moduleUrl, context, defaultLoad); const code = fs.readFileSync(filename, 'utf-8'); @@ -68,8 +68,8 @@ let transport: PortTransport | undefined; function globalPreload(context: { port: MessagePort }) { transport = new PortTransport(context.port, async (method, params) => { - if (method === 'setBabelPlugins') { - setBabelPlugins(params.plugins); + if (method === 'setTransformConfig') { + setTransformConfig(params.config); return; } diff --git a/packages/playwright-test/src/transform/transform.ts b/packages/playwright-test/src/transform/transform.ts index bd0ae9078b..9b29991660 100644 --- a/packages/playwright-test/src/transform/transform.ts +++ b/packages/playwright-test/src/transform/transform.ts @@ -23,7 +23,8 @@ import type { TsConfigLoaderResult } from '../third_party/tsconfig-loader'; import { tsConfigLoader } from '../third_party/tsconfig-loader'; import Module from 'module'; import type { BabelPlugin, BabelTransformFunction } from './babelBundle'; -import { fileIsModule, resolveImportSpecifierExtension } from '../util'; +import { createFileMatcher, fileIsModule, resolveImportSpecifierExtension } from '../util'; +import type { Matcher } from '../util'; import { getFromCompilationCache, currentFileDepsCollector, belongsToNodeModules } from './compilationCache'; const version = require('../../package.json').version; @@ -35,14 +36,25 @@ type ParsedTsConfigData = { }; const cachedTSConfigs = new Map(); -let babelPlugins: BabelPlugin[] = []; +export type TransformConfig = { + babelPlugins: [string, any?][]; + external: string[]; +}; -export function setBabelPlugins(plugins: BabelPlugin[]) { - babelPlugins = plugins; +let _transformConfig: TransformConfig = { + babelPlugins: [], + external: [], +}; + +let _externalMatcher: Matcher = () => false; + +export function setTransformConfig(config: TransformConfig) { + _transformConfig = config; + _externalMatcher = createFileMatcher(_transformConfig.external); } -export function getBabelPlugins(): BabelPlugin[] { - return babelPlugins; +export function transformConfig(): TransformConfig { + return _transformConfig; } function validateTsConfig(tsconfig: TsConfigLoaderResult): ParsedTsConfigData | undefined { @@ -75,7 +87,7 @@ const builtins = new Set(Module.builtinModules); export function resolveHook(filename: string, specifier: string): string | undefined { if (specifier.startsWith('node:') || builtins.has(specifier)) return; - if (belongsToNodeModules(filename)) + if (!shouldTransform(filename)) return; if (isRelativeSpecifier(specifier)) @@ -138,13 +150,19 @@ export function resolveHook(filename: string, specifier: string): string | undef } } +export function shouldTransform(filename: string): boolean { + if (_externalMatcher(filename)) + return false; + return !belongsToNodeModules(filename); +} + export function transformHook(originalCode: string, filename: string, moduleUrl?: string): string { 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 && process.env.PW_TEST_SOURCE_TRANSFORM_SCOPE.split(pathSeparator).some(f => filename.startsWith(f)); - const pluginsPrologue = babelPlugins; + const pluginsPrologue = _transformConfig.babelPlugins; const pluginsEpilogue = hasPreprocessor ? [[process.env.PW_TEST_SOURCE_TRANSFORM!]] as BabelPlugin[] : []; const hash = calculateHash(originalCode, filename, !!moduleUrl, pluginsPrologue, pluginsEpilogue); const { cachedCode, addToCache } = getFromCompilationCache(filename, hash, moduleUrl); @@ -209,7 +227,7 @@ function installTransform(): () => void { (Module as any)._resolveFilename = resolveFilename; const revertPirates = pirates.addHook((code: string, filename: string) => { - if (belongsToNodeModules(filename)) + if (!shouldTransform(filename)) return code; return transformHook(code, filename); }, { exts: ['.ts', '.tsx', '.js', '.jsx', '.mjs'] }); diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index c5905e9851..193b569c5f 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -569,6 +569,31 @@ interface TestConfig { * */ webServer?: TestConfigWebServer | TestConfigWebServer[]; + /** + * Transpiler configuration. + * + * **Usage** + * + * ```js + * // playwright.config.ts + * import { defineConfig } from '@playwright/test'; + * + * export default defineConfig({ + * build: { + * external: '**\/*bundle.js', + * }, + * }); + * ``` + * + */ + build?: { + /** + * Paths to exclude from the transpilation expressed as glob patterns. Typically heavy JS bundles your tests + * reference. + */ + external?: Array; + }; + /** * Configuration for the `expect` assertion library. Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts). * diff --git a/tests/playwright-test/babel.spec.ts b/tests/playwright-test/babel.spec.ts index bd932c7601..3fb0a1a725 100644 --- a/tests/playwright-test/babel.spec.ts +++ b/tests/playwright-test/babel.spec.ts @@ -131,3 +131,22 @@ test('should not read browserslist file', async ({ runInlineTest }) => { expect(result.passed).toBe(1); expect(result.failed).toBe(0); }); + +test('should not transform external', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + import { defineConfig } from '@playwright/test'; + export default defineConfig({ + build: { + external: ['**/a.spec.ts'] + } + }); + `, + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('succeeds', () => {}); + ` + }); + expect(result.exitCode).toBe(1); + expect(result.output).toContain('Cannot use import statement outside a module'); +});