diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index 2eb80e9acf..e8ac912802 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -492,7 +492,7 @@ export default defineConfig({ Only the files matching one of these patterns are executed as test files. Matching is performed against the absolute file path. Strings are treated as glob patterns. -By default, Playwright looks for files matching the following glob pattern: `**/*.@(spec|test).?(m)[jt]s?(x)`. This means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example `login-screen.wrong-credentials.spec.ts`. +By default, Playwright looks for files matching the following glob pattern: `**/*.@(spec|test).?(c|m)[jt]s?(x)`. This means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example `login-screen.wrong-credentials.spec.ts`. **Usage** diff --git a/docs/src/test-api/class-testproject.md b/docs/src/test-api/class-testproject.md index 7e9d2b0139..c19d113e42 100644 --- a/docs/src/test-api/class-testproject.md +++ b/docs/src/test-api/class-testproject.md @@ -305,7 +305,7 @@ Use [`property: TestConfig.testIgnore`] to change this option for all projects. Only the files matching one of these patterns are executed as test files. Matching is performed against the absolute file path. Strings are treated as glob patterns. -By default, Playwright looks for files matching the following glob pattern: `**/*.@(spec|test).?(m)[jt]s?(x)`. This means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example `login-screen.wrong-credentials.spec.ts`. +By default, Playwright looks for files matching the following glob pattern: `**/*.@(spec|test).?(c|m)[jt]s?(x)`. This means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example `login-screen.wrong-credentials.spec.ts`. Use [`property: TestConfig.testMatch`] to change this option for all projects. diff --git a/packages/playwright-test/src/cli.ts b/packages/playwright-test/src/cli.ts index 0b4feae876..b68cbf27c1 100644 --- a/packages/playwright-test/src/cli.ts +++ b/packages/playwright-test/src/cli.ts @@ -24,7 +24,7 @@ import { stopProfiling, startProfiling } from 'playwright-core/lib/utils'; import { experimentalLoaderOption, fileIsModule, serializeError } from './util'; import { showHTMLReport } from './reporters/html'; import { createMergedReport } from './reporters/merge'; -import { ConfigLoader, kDefaultConfigFiles, resolveConfigFile } from './common/configLoader'; +import { ConfigLoader, resolveConfigFile } from './common/configLoader'; import type { ConfigCLIOverrides } from './common/ipc'; import type { FullResult, TestError } from '../reporter'; import type { TraceMode } from '../types/test'; @@ -61,7 +61,7 @@ Examples: function addListFilesCommand(program: Command) { const command = program.command('list-files [file-filter...]', { hidden: true }); command.description('List files with Playwright Test tests'); - command.option('-c, --config ', `Configuration file, or a test directory with optional ${kDefaultConfigFiles.map(file => `"${file}"`).join('/')}`); + command.option('-c, --config ', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`); command.option('--project ', `Only run tests from the specified list of projects (default: list all projects)`); command.action(async (args, opts) => { try { @@ -297,7 +297,7 @@ const kTraceModes: TraceMode[] = ['on', 'off', 'on-first-retry', 'on-all-retries const testOptions: [string, string][] = [ ['--browser ', `Browser to use for tests, one of "all", "chromium", "firefox" or "webkit" (default: "chromium")`], - ['-c, --config ', `Configuration file, or a test directory with optional ${kDefaultConfigFiles.map(file => `"${file}"`).join('/')}`], + ['-c, --config ', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`], ['--debug', `Run tests with Playwright Inspector. Shortcut for "PWDEBUG=1" environment variable and "--timeout=0 --max-failures=1 --headed --workers=1" options`], ['--forbid-only', `Fail if test.only is called (default: false)`], ['--fully-parallel', `Run all tests in parallel (default: false)`], diff --git a/packages/playwright-test/src/common/config.ts b/packages/playwright-test/src/common/config.ts index 6de5ea0e68..bd4a7d4473 100644 --- a/packages/playwright-test/src/common/config.ts +++ b/packages/playwright-test/src/common/config.ts @@ -173,7 +173,7 @@ export class FullProjectInternal { testDir, snapshotDir: takeFirst(pathResolve(configDir, projectConfig.snapshotDir), pathResolve(configDir, config.snapshotDir), testDir), testIgnore: takeFirst(projectConfig.testIgnore, config.testIgnore, []), - testMatch: takeFirst(projectConfig.testMatch, config.testMatch, '**/*.@(spec|test).?(m)[jt]s?(x)'), + testMatch: takeFirst(projectConfig.testMatch, config.testMatch, '**/*.@(spec|test).?(c|m)[jt]s?(x)'), timeout: takeFirst(configCLIOverrides.timeout, projectConfig.timeout, config.timeout, defaultTimeout), use: mergeObjects(config.use, projectConfig.use, configCLIOverrides.use), dependencies: projectConfig.dependencies || [], diff --git a/packages/playwright-test/src/common/configLoader.ts b/packages/playwright-test/src/common/configLoader.ts index 02006183c2..e2f74a0ced 100644 --- a/packages/playwright-test/src/common/configLoader.ts +++ b/packages/playwright-test/src/common/configLoader.ts @@ -258,8 +258,6 @@ function validateProject(file: string, project: Project, title: string) { } } -export const kDefaultConfigFiles = ['playwright.config.ts', 'playwright.config.js', 'playwright.config.mjs']; - export function resolveConfigFile(configFileOrDirectory: string): string | null { const resolveConfig = (configFile: string) => { if (fs.existsSync(configFile)) @@ -267,8 +265,8 @@ export function resolveConfigFile(configFileOrDirectory: string): string | null }; const resolveConfigFileFromDirectory = (directory: string) => { - for (const configName of kDefaultConfigFiles) { - const configFile = resolveConfig(path.resolve(directory, configName)); + for (const ext of ['.ts', '.js', '.mts', '.mjs', '.cts', '.cjs']) { + const configFile = resolveConfig(path.resolve(directory, 'playwright.config' + ext)); if (configFile) return configFile; } diff --git a/packages/playwright-test/src/runner/projectUtils.ts b/packages/playwright-test/src/runner/projectUtils.ts index d009323c39..5c9e3794dc 100644 --- a/packages/playwright-test/src/runner/projectUtils.ts +++ b/packages/playwright-test/src/runner/projectUtils.ts @@ -102,8 +102,8 @@ export function buildDependentProjects(forProject: FullProjectInternal, projects } export async function collectFilesForProject(project: FullProjectInternal, fsCache = new Map()): Promise { - const extensions = ['.js', '.ts', '.mjs', '.tsx', '.jsx']; - const testFileExtension = (file: string) => extensions.includes(path.extname(file)); + const extensions = new Set(['.js', '.ts', '.mjs', '.mts', '.cjs', '.cts', '.jsx', '.tsx', '.mjsx', '.mtsx', '.cjsx', '.ctsx']); + const testFileExtension = (file: string) => extensions.has(path.extname(file)); const allFiles = await cachedCollectFiles(project.project.testDir, project.respectGitIgnore, fsCache); const testMatch = createFileMatcher(project.project.testMatch); const testIgnore = createFileMatcher(project.project.testIgnore); diff --git a/packages/playwright-test/src/util.ts b/packages/playwright-test/src/util.ts index df64becd94..1b7083220f 100644 --- a/packages/playwright-test/src/util.ts +++ b/packages/playwright-test/src/util.ts @@ -280,9 +280,10 @@ export async function normalizeAndSaveAttachment(outputPath: string, name: strin } export function fileIsModule(file: string): boolean { - if (file.endsWith('.mjs')) + if (file.endsWith('.mjs') || file.endsWith('.mts')) return true; - + if (file.endsWith('.cjs') || file.endsWith('.cts')) + return false; const folder = path.dirname(file); return folderIsModule(folder); } diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index b6c5cfd5df..5f477f86b0 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -395,8 +395,8 @@ export interface FullProject { * Only the files matching one of these patterns are executed as test files. Matching is performed against the * absolute file path. Strings are treated as glob patterns. * - * By default, Playwright looks for files matching the following glob pattern: `**\/*.@(spec|test).?(m)[jt]s?(x)`. This - * means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example + * By default, Playwright looks for files matching the following glob pattern: `**\/*.@(spec|test).?(c|m)[jt]s?(x)`. + * This means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example * `login-screen.wrong-credentials.spec.ts`. * * Use [testConfig.testMatch](https://playwright.dev/docs/api/class-testconfig#test-config-test-match) to change this @@ -1240,8 +1240,8 @@ interface TestConfig { * Only the files matching one of these patterns are executed as test files. Matching is performed against the * absolute file path. Strings are treated as glob patterns. * - * By default, Playwright looks for files matching the following glob pattern: `**\/*.@(spec|test).?(m)[jt]s?(x)`. This - * means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example + * By default, Playwright looks for files matching the following glob pattern: `**\/*.@(spec|test).?(c|m)[jt]s?(x)`. + * This means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example * `login-screen.wrong-credentials.spec.ts`. * * **Usage** @@ -6439,8 +6439,8 @@ interface TestProject { * Only the files matching one of these patterns are executed as test files. Matching is performed against the * absolute file path. Strings are treated as glob patterns. * - * By default, Playwright looks for files matching the following glob pattern: `**\/*.@(spec|test).?(m)[jt]s?(x)`. This - * means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example + * By default, Playwright looks for files matching the following glob pattern: `**\/*.@(spec|test).?(c|m)[jt]s?(x)`. + * This means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example * `login-screen.wrong-credentials.spec.ts`. * * Use [testConfig.testMatch](https://playwright.dev/docs/api/class-testconfig#test-config-test-match) to change this diff --git a/tests/playwright-test/esm.spec.ts b/tests/playwright-test/esm.spec.ts index 6053244545..641b27765b 100644 --- a/tests/playwright-test/esm.spec.ts +++ b/tests/playwright-test/esm.spec.ts @@ -392,3 +392,47 @@ test('should resolve .js import to .tsx file in ESM mode for components', async expect(result.passed).toBe(1); expect(result.exitCode).toBe(0); }); + +test('should load cjs config and test in non-ESM mode', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'package.json': `{ "type": "module" }`, + 'playwright.config.cjs': ` + const fs = require('fs'); + module.exports = { projects: [{name: 'foo'}] }; + `, + 'a.test.js': ` + import { test, expect } from '@playwright/test'; + test('check project name', ({}, testInfo) => { + expect(testInfo.project.name).toBe('foo'); + }); + `, + 'b.spec.cjs': ` + const { test, expect } = require('@playwright/test'); + test('check project name', ({}, testInfo) => { + expect(testInfo.project.name).toBe('foo'); + }); + `, + }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(2); +}); + +test('should disallow ESM when config is cjs', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'package.json': `{ "type": "module" }`, + 'playwright.config.cjs': ` + const fs = require('fs'); + module.exports = { projects: [{name: 'foo'}] }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('check project name', ({}, testInfo) => { + expect(testInfo.project.name).toBe('foo'); + }); + `, + }); + + expect(result.exitCode).toBe(1); + expect(result.output).toContain('Unknown file extension ".ts"'); +}); diff --git a/tests/playwright-test/loader.spec.ts b/tests/playwright-test/loader.spec.ts index 8c0caaf868..774e67f057 100644 --- a/tests/playwright-test/loader.spec.ts +++ b/tests/playwright-test/loader.spec.ts @@ -224,7 +224,7 @@ test('should load esm when package.json has type module', async ({ runInlineTest expect(result.passed).toBe(1); }); -test('should load esm config files', async ({ runInlineTest }) => { +test('should load mjs config file', async ({ runInlineTest }) => { const result = await runInlineTest({ 'playwright.config.mjs': ` import * as fs from 'fs'; @@ -242,6 +242,24 @@ test('should load esm config files', async ({ runInlineTest }) => { expect(result.passed).toBe(1); }); +test('should load mts config file', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.mts': ` + import * as fs from 'fs'; + export default { projects: [{name: 'foo'}] }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('check project name', ({}, testInfo) => { + expect(testInfo.project.name).toBe('foo'); + }); + ` + }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); +}); + test('should load ts from esm when package.json has type module', async ({ runInlineTest, nodeVersion }) => { // We only support experimental esm mode on Node 16+ test.skip(nodeVersion.major < 16);