fix(test runner): resolve tsconfig for each file (#11662)
This allows us to properly handle path mappings that are not too ambiguous.
This commit is contained in:
parent
b17f2a86da
commit
b1fbc4fdbe
|
|
@ -15,12 +15,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
|
||||||
import { tsConfigLoader, TsConfigLoaderResult } from './third_party/tsconfig-loader';
|
|
||||||
import { transformHook } from './transform';
|
import { transformHook } from './transform';
|
||||||
|
|
||||||
const tsConfigCache = new Map<string, TsConfigLoaderResult>();
|
|
||||||
|
|
||||||
async function resolve(specifier: string, context: { parentURL: string }, defaultResolve: any) {
|
async function resolve(specifier: string, context: { parentURL: string }, defaultResolve: any) {
|
||||||
if (specifier.endsWith('.js') || specifier.endsWith('.ts') || specifier.endsWith('.mjs'))
|
if (specifier.endsWith('.js') || specifier.endsWith('.ts') || specifier.endsWith('.mjs'))
|
||||||
return defaultResolve(specifier, context, defaultResolve);
|
return defaultResolve(specifier, context, defaultResolve);
|
||||||
|
|
@ -36,17 +32,8 @@ async function resolve(specifier: string, context: { parentURL: string }, defaul
|
||||||
async function load(url: string, context: any, defaultLoad: any) {
|
async function load(url: string, context: any, defaultLoad: any) {
|
||||||
if (url.endsWith('.ts') || url.endsWith('.tsx')) {
|
if (url.endsWith('.ts') || url.endsWith('.tsx')) {
|
||||||
const filename = url.substring('file://'.length);
|
const filename = url.substring('file://'.length);
|
||||||
const cwd = path.dirname(filename);
|
|
||||||
let tsconfig = tsConfigCache.get(cwd);
|
|
||||||
if (!tsconfig) {
|
|
||||||
tsconfig = tsConfigLoader({
|
|
||||||
getEnv: (name: string) => process.env[name],
|
|
||||||
cwd
|
|
||||||
});
|
|
||||||
tsConfigCache.set(cwd, tsconfig);
|
|
||||||
}
|
|
||||||
const code = fs.readFileSync(filename, 'utf-8');
|
const code = fs.readFileSync(filename, 'utf-8');
|
||||||
const source = transformHook(code, filename, tsconfig, true);
|
const source = transformHook(code, filename, true);
|
||||||
return { format: 'module', source };
|
return { format: 'module', source };
|
||||||
}
|
}
|
||||||
return defaultLoad(url, context, defaultLoad);
|
return defaultLoad(url, context, defaultLoad);
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,10 @@ import { ProjectImpl } from './project';
|
||||||
import { Reporter } from '../types/testReporter';
|
import { Reporter } from '../types/testReporter';
|
||||||
import { BuiltInReporter, builtInReporters } from './runner';
|
import { BuiltInReporter, builtInReporters } from './runner';
|
||||||
import { isRegExp } from 'playwright-core/lib/utils/utils';
|
import { isRegExp } from 'playwright-core/lib/utils/utils';
|
||||||
import { tsConfigLoader, TsConfigLoaderResult } from './third_party/tsconfig-loader';
|
|
||||||
|
|
||||||
// To allow multiple loaders in the same process without clearing require cache,
|
// To allow multiple loaders in the same process without clearing require cache,
|
||||||
// we make these maps global.
|
// we make these maps global.
|
||||||
const cachedFileSuites = new Map<string, Suite>();
|
const cachedFileSuites = new Map<string, Suite>();
|
||||||
const cachedTSConfigs = new Map<string, TsConfigLoaderResult>();
|
|
||||||
|
|
||||||
export class Loader {
|
export class Loader {
|
||||||
private _defaultConfig: Config;
|
private _defaultConfig: Config;
|
||||||
|
|
@ -194,18 +192,7 @@ export class Loader {
|
||||||
|
|
||||||
|
|
||||||
private async _requireOrImport(file: string) {
|
private async _requireOrImport(file: string) {
|
||||||
// Respect tsconfig paths.
|
const revertBabelRequire = installTransform();
|
||||||
const cwd = path.dirname(file);
|
|
||||||
let tsconfig = cachedTSConfigs.get(cwd);
|
|
||||||
if (!tsconfig) {
|
|
||||||
tsconfig = tsConfigLoader({
|
|
||||||
getEnv: (name: string) => process.env[name],
|
|
||||||
cwd
|
|
||||||
});
|
|
||||||
cachedTSConfigs.set(cwd, tsconfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
const revertBabelRequire = installTransform(tsconfig);
|
|
||||||
|
|
||||||
// Figure out if we are importing or requiring.
|
// Figure out if we are importing or requiring.
|
||||||
let isModule: boolean;
|
let isModule: boolean;
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,20 @@ import * as pirates from 'pirates';
|
||||||
import * as sourceMapSupport from 'source-map-support';
|
import * as sourceMapSupport from 'source-map-support';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import type { Location } from './types';
|
import type { Location } from './types';
|
||||||
import { TsConfigLoaderResult } from './third_party/tsconfig-loader';
|
import { tsConfigLoader, TsConfigLoaderResult } from './third_party/tsconfig-loader';
|
||||||
|
|
||||||
const version = 6;
|
const version = 6;
|
||||||
const cacheDir = process.env.PWTEST_CACHE_DIR || path.join(os.tmpdir(), 'playwright-transform-cache');
|
const cacheDir = process.env.PWTEST_CACHE_DIR || path.join(os.tmpdir(), 'playwright-transform-cache');
|
||||||
const sourceMaps: Map<string, string> = new Map();
|
const sourceMaps: Map<string, string> = new Map();
|
||||||
|
|
||||||
|
type ParsedTsConfigData = {
|
||||||
|
absoluteBaseUrl: string,
|
||||||
|
singlePath: { [key: string]: string },
|
||||||
|
hash: string,
|
||||||
|
alias: { [key: string]: string | ((s: string[]) => string) },
|
||||||
|
};
|
||||||
|
const cachedTSConfigs = new Map<string, ParsedTsConfigData | undefined>();
|
||||||
|
|
||||||
const kStackTraceLimit = 15;
|
const kStackTraceLimit = 15;
|
||||||
Error.stackTraceLimit = kStackTraceLimit;
|
Error.stackTraceLimit = kStackTraceLimit;
|
||||||
|
|
||||||
|
|
@ -47,9 +55,9 @@ sourceMapSupport.install({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function calculateCachePath(tsconfig: TsConfigLoaderResult, content: string, filePath: string): string {
|
function calculateCachePath(tsconfigData: ParsedTsConfigData | undefined, content: string, filePath: string): string {
|
||||||
const hash = crypto.createHash('sha1')
|
const hash = crypto.createHash('sha1')
|
||||||
.update(tsconfig.serialized || '')
|
.update(tsconfigData?.hash || '')
|
||||||
.update(process.env.PW_EXPERIMENTAL_TS_ESM ? 'esm' : 'no_esm')
|
.update(process.env.PW_EXPERIMENTAL_TS_ESM ? 'esm' : 'no_esm')
|
||||||
.update(content)
|
.update(content)
|
||||||
.update(filePath)
|
.update(filePath)
|
||||||
|
|
@ -59,10 +67,64 @@ function calculateCachePath(tsconfig: TsConfigLoaderResult, content: string, fil
|
||||||
return path.join(cacheDir, hash[0] + hash[1], fileName);
|
return path.join(cacheDir, hash[0] + hash[1], fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformHook(code: string, filename: string, tsconfig: TsConfigLoaderResult, isModule = false): string {
|
function validateTsConfig(tsconfig: TsConfigLoaderResult): ParsedTsConfigData | undefined {
|
||||||
|
if (!tsconfig.tsConfigPath || !tsconfig.paths || !tsconfig.baseUrl)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const paths = tsconfig.paths;
|
||||||
|
// Path that only contains "*", ".", "/" and "\" is too ambiguous.
|
||||||
|
const ambiguousPath = Object.keys(paths).find(key => key.match(/^[*./\\]+$/));
|
||||||
|
if (ambiguousPath)
|
||||||
|
return;
|
||||||
|
const multiplePath = Object.keys(paths).find(key => paths[key].length > 1);
|
||||||
|
if (multiplePath)
|
||||||
|
return;
|
||||||
|
// Only leave a single path mapping.
|
||||||
|
const singlePath = Object.fromEntries(Object.entries(paths).map(([key, values]) => ([key, values[0]])));
|
||||||
|
// Make 'baseUrl' absolute, because it is relative to the tsconfig.json, not to cwd.
|
||||||
|
const absoluteBaseUrl = path.resolve(path.dirname(tsconfig.tsConfigPath), tsconfig.baseUrl);
|
||||||
|
const hash = JSON.stringify({ absoluteBaseUrl, singlePath });
|
||||||
|
|
||||||
|
const alias: ParsedTsConfigData['alias'] = {};
|
||||||
|
for (const [key, value] of Object.entries(singlePath)) {
|
||||||
|
const regexKey = '^' + key.replace('*', '.*');
|
||||||
|
alias[regexKey] = ([name]) => {
|
||||||
|
let relative: string;
|
||||||
|
if (key.endsWith('/*'))
|
||||||
|
relative = value.substring(0, value.length - 1) + name.substring(key.length - 1);
|
||||||
|
else
|
||||||
|
relative = value;
|
||||||
|
relative = relative.replace(/\//g, path.sep);
|
||||||
|
return path.resolve(absoluteBaseUrl, relative);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
absoluteBaseUrl,
|
||||||
|
singlePath,
|
||||||
|
hash,
|
||||||
|
alias,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadAndValidateTsconfigForFile(file: string): ParsedTsConfigData | undefined {
|
||||||
|
const cwd = path.dirname(file);
|
||||||
|
if (!cachedTSConfigs.has(cwd)) {
|
||||||
|
const loaded = tsConfigLoader({
|
||||||
|
getEnv: (name: string) => process.env[name],
|
||||||
|
cwd
|
||||||
|
});
|
||||||
|
cachedTSConfigs.set(cwd, validateTsConfig(loaded));
|
||||||
|
}
|
||||||
|
return cachedTSConfigs.get(cwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transformHook(code: string, filename: string, isModule = false): string {
|
||||||
if (isComponentImport(filename))
|
if (isComponentImport(filename))
|
||||||
return componentStub();
|
return componentStub();
|
||||||
const cachePath = calculateCachePath(tsconfig, code, filename);
|
|
||||||
|
const tsconfigData = loadAndValidateTsconfigForFile(filename);
|
||||||
|
const cachePath = calculateCachePath(tsconfigData, code, filename);
|
||||||
const codePath = cachePath + '.js';
|
const codePath = cachePath + '.js';
|
||||||
const sourceMapPath = cachePath + '.map';
|
const sourceMapPath = cachePath + '.map';
|
||||||
sourceMaps.set(filename, sourceMapPath);
|
sourceMaps.set(filename, sourceMapPath);
|
||||||
|
|
@ -73,30 +135,6 @@ export function transformHook(code: string, filename: string, tsconfig: TsConfig
|
||||||
process.env.BROWSERSLIST_IGNORE_OLD_DATA = 'true';
|
process.env.BROWSERSLIST_IGNORE_OLD_DATA = 'true';
|
||||||
const babel: typeof import('@babel/core') = require('@babel/core');
|
const babel: typeof import('@babel/core') = require('@babel/core');
|
||||||
|
|
||||||
const hasBaseUrl = !!tsconfig.baseUrl;
|
|
||||||
const extensions = ['', '.js', '.ts', '.mjs', ...(process.env.PW_COMPONENT_TESTING ? ['.tsx', '.jsx'] : [])]; const alias: { [key: string]: string | ((s: string[]) => string) } = {};
|
|
||||||
for (const [key, values] of Object.entries(tsconfig.paths || { '*': '*' })) {
|
|
||||||
const regexKey = '^' + key.replace('*', '.*');
|
|
||||||
alias[regexKey] = ([name]) => {
|
|
||||||
for (const value of values) {
|
|
||||||
let relative: string;
|
|
||||||
if (key === '*' && value === '*')
|
|
||||||
relative = name;
|
|
||||||
else if (key.endsWith('/*'))
|
|
||||||
relative = value.substring(0, value.length - 1) + name.substring(key.length - 1);
|
|
||||||
else
|
|
||||||
relative = value;
|
|
||||||
relative = relative.replace(/\//g, path.sep);
|
|
||||||
const result = path.resolve(tsconfig.baseUrl || '', relative);
|
|
||||||
for (const extension of extensions) {
|
|
||||||
if (fs.existsSync(result + extension))
|
|
||||||
return result + extension;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
[require.resolve('@babel/plugin-proposal-class-properties')],
|
[require.resolve('@babel/plugin-proposal-class-properties')],
|
||||||
[require.resolve('@babel/plugin-proposal-numeric-separator')],
|
[require.resolve('@babel/plugin-proposal-numeric-separator')],
|
||||||
|
|
@ -110,10 +148,13 @@ export function transformHook(code: string, filename: string, tsconfig: TsConfig
|
||||||
[require.resolve('@babel/plugin-proposal-export-namespace-from')],
|
[require.resolve('@babel/plugin-proposal-export-namespace-from')],
|
||||||
] as any;
|
] as any;
|
||||||
|
|
||||||
if (hasBaseUrl) {
|
if (tsconfigData) {
|
||||||
plugins.push([require.resolve('babel-plugin-module-resolver'), {
|
plugins.push([require.resolve('babel-plugin-module-resolver'), {
|
||||||
root: ['./'],
|
root: ['./'],
|
||||||
alias
|
alias: tsconfigData.alias,
|
||||||
|
// Silences warning 'Could not resovle ...' that we trigger because we resolve
|
||||||
|
// into 'foo/bar', and not 'foo/bar.ts'.
|
||||||
|
loglevel: 'silent',
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,16 +184,13 @@ export function transformHook(code: string, filename: string, tsconfig: TsConfig
|
||||||
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
||||||
if (result.map)
|
if (result.map)
|
||||||
fs.writeFileSync(sourceMapPath, JSON.stringify(result.map), 'utf8');
|
fs.writeFileSync(sourceMapPath, JSON.stringify(result.map), 'utf8');
|
||||||
// Compiled files with base URL depend on the FS state during compilation,
|
fs.writeFileSync(codePath, result.code, 'utf8');
|
||||||
// never cache them.
|
|
||||||
if (!hasBaseUrl)
|
|
||||||
fs.writeFileSync(codePath, result.code, 'utf8');
|
|
||||||
}
|
}
|
||||||
return result.code || '';
|
return result.code || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function installTransform(tsconfig: TsConfigLoaderResult): () => void {
|
export function installTransform(): () => void {
|
||||||
return pirates.addHook((code: string, filename: string) => transformHook(code, filename, tsconfig), { exts: ['.ts', '.tsx'] });
|
return pirates.addHook((code: string, filename: string) => transformHook(code, filename), { exts: ['.ts', '.tsx'] });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapFunctionWithLocation<A extends any[], R>(func: (location: Location, ...args: A) => R): (...args: A) => R {
|
export function wrapFunctionWithLocation<A extends any[], R>(func: (location: Location, ...args: A) => R): (...args: A) => R {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { getUserAgent } from 'playwright-core/lib/utils/utils';
|
import { getUserAgent } from '../packages/playwright-core/lib/utils/utils';
|
||||||
import WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
import { expect, playwrightTest as test } from './config/browserTest';
|
import { expect, playwrightTest as test } from './config/browserTest';
|
||||||
import { parseTrace, suppressCertificateWarning } from './config/utils';
|
import { parseTrace, suppressCertificateWarning } from './config/utils';
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import { contextTest as test, expect } from '../config/browserTest';
|
||||||
import { playwrightTest } from '../config/browserTest';
|
import { playwrightTest } from '../config/browserTest';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { getUserAgent } from 'playwright-core/lib/utils/utils';
|
import { getUserAgent } from '../../packages/playwright-core/lib/utils/utils';
|
||||||
import { suppressCertificateWarning } from '../config/utils';
|
import { suppressCertificateWarning } from '../config/utils';
|
||||||
|
|
||||||
test('should create a worker from a service worker', async ({ page, server }) => {
|
test('should create a worker from a service worker', async ({ page, server }) => {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import * as os from 'os';
|
||||||
import { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi';
|
import { PageTestFixtures, PageWorkerFixtures } from '../page/pageTestApi';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import type { BrowserContext, BrowserContextOptions, BrowserType, Page } from 'playwright-core';
|
import type { BrowserContext, BrowserContextOptions, BrowserType, Page } from 'playwright-core';
|
||||||
import { removeFolders } from 'playwright-core/lib/utils/utils';
|
import { removeFolders } from '../../packages/playwright-core/lib/utils/utils';
|
||||||
import { baseTest } from './baseTest';
|
import { baseTest } from './baseTest';
|
||||||
import { RemoteServer, RemoteServerOptions } from './remoteServer';
|
import { RemoteServer, RemoteServerOptions } from './remoteServer';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import { getPlaywrightVersion } from 'playwright-core/lib/utils/utils';
|
import { getPlaywrightVersion } from '../packages/playwright-core/lib/utils/utils';
|
||||||
import { expect, playwrightTest as it } from './config/browserTest';
|
import { expect, playwrightTest as it } from './config/browserTest';
|
||||||
|
|
||||||
it.skip(({ mode }) => mode !== 'default');
|
it.skip(({ mode }) => mode !== 'default');
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import fs from 'fs';
|
||||||
import http2 from 'http2';
|
import http2 from 'http2';
|
||||||
import type { BrowserContext, BrowserContextOptions } from 'playwright-core';
|
import type { BrowserContext, BrowserContextOptions } from 'playwright-core';
|
||||||
import type { AddressInfo } from 'net';
|
import type { AddressInfo } from 'net';
|
||||||
import type { Log } from 'playwright-core/lib/server/supplements/har/har';
|
import type { Log } from '../packages/playwright-core/src/server/supplements/har/har';
|
||||||
|
|
||||||
async function pageWithHar(contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>, testInfo: any, outputPath: string = 'test.har') {
|
async function pageWithHar(contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>, testInfo: any, outputPath: string = 'test.har') {
|
||||||
const harPath = testInfo.outputPath(outputPath);
|
const harPath = testInfo.outputPath(outputPath);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
import { contextTest } from '../config/browserTest';
|
import { contextTest } from '../config/browserTest';
|
||||||
import type { Page } from 'playwright-core';
|
import type { Page } from 'playwright-core';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import type { Source } from 'playwright-core/lib/server/supplements/recorder/recorderTypes';
|
import type { Source } from '../../packages/playwright-core/src/server/supplements/recorder/recorderTypes';
|
||||||
import { CommonFixtures, TestChildProcess } from '../config/commonFixtures';
|
import { CommonFixtures, TestChildProcess } from '../config/commonFixtures';
|
||||||
export { expect } from '@playwright/test';
|
export { expect } from '@playwright/test';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { spawnSync } from 'child_process';
|
import { spawnSync } from 'child_process';
|
||||||
import { registry } from 'playwright-core/lib/utils/registry';
|
import { registry } from '../../packages/playwright-core/lib/utils/registry';
|
||||||
|
|
||||||
const ffmpeg = registry.findExecutable('ffmpeg')!.executablePath();
|
const ffmpeg = registry.findExecutable('ffmpeg')!.executablePath();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test as baseTest, expect } from './playwright-test-fixtures';
|
import { test as baseTest, expect } from './playwright-test-fixtures';
|
||||||
import { HttpServer } from 'playwright-core/lib/utils/httpServer';
|
import { HttpServer } from '../../packages/playwright-core/lib/utils/httpServer';
|
||||||
import { startHtmlReportServer } from '../../packages/playwright-test/lib/reporters/html';
|
import { startHtmlReportServer } from '../../packages/playwright-test/lib/reporters/html';
|
||||||
|
|
||||||
const test = baseTest.extend<{ showReport: () => Promise<void> }>({
|
const test = baseTest.extend<{ showReport: () => Promise<void> }>({
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
import { test, expect } from './playwright-test-fixtures';
|
import { test, expect } from './playwright-test-fixtures';
|
||||||
|
|
||||||
test('should respect path resolver', async ({ runInlineTest }) => {
|
test('should respect path resolver', async ({ runInlineTest }) => {
|
||||||
|
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/11656' });
|
||||||
|
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -31,7 +33,7 @@ test('should respect path resolver', async ({ runInlineTest }) => {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"util/*": ["./foo/bar/util/*"],
|
"util/*": ["./foo/bar/util/*"],
|
||||||
"util2/*": ["./non-existent/*", "./foo/bar/util/*"],
|
"util2/*": ["./foo/bar/util/*"],
|
||||||
"util3": ["./foo/bar/util/b"],
|
"util3": ["./foo/bar/util/b"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -60,10 +62,36 @@ test('should respect path resolver', async ({ runInlineTest }) => {
|
||||||
'foo/bar/util/b.ts': `
|
'foo/bar/util/b.ts': `
|
||||||
export const foo: string = 'foo';
|
export const foo: string = 'foo';
|
||||||
`,
|
`,
|
||||||
|
'helper.ts': `
|
||||||
|
export { foo } from 'util3';
|
||||||
|
`,
|
||||||
|
'dir/tsconfig.json': `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2019",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["esnext", "dom", "DOM.Iterable"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"parent-util/*": ["../foo/bar/util/*"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}`,
|
||||||
|
'dir/inner.spec.ts': `
|
||||||
|
// This import should pick up <root>/dir/tsconfig
|
||||||
|
import { foo } from 'parent-util/b';
|
||||||
|
// This import should pick up <root>/tsconfig through the helper
|
||||||
|
import { foo as foo2 } from '../helper';
|
||||||
|
const { test } = pwt;
|
||||||
|
test('test', ({}, testInfo) => {
|
||||||
|
expect(testInfo.project.name).toBe(foo);
|
||||||
|
expect(testInfo.project.name).toBe(foo2);
|
||||||
|
});
|
||||||
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.passed).toBe(3);
|
expect(result.passed).toBe(4);
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.output).not.toContain(`Could not`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should respect baseurl', async ({ runInlineTest }) => {
|
test('should respect baseurl', async ({ runInlineTest }) => {
|
||||||
|
|
@ -108,13 +136,8 @@ test('should respect baseurl', async ({ runInlineTest }) => {
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should respect baseurl w/o paths', async ({ runInlineTest }) => {
|
test('should ignore for baseurl w/o paths', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'playwright.config.ts': `
|
|
||||||
export default {
|
|
||||||
projects: [{name: 'foo'}],
|
|
||||||
};
|
|
||||||
`,
|
|
||||||
'tsconfig.json': `{
|
'tsconfig.json': `{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2019",
|
"target": "ES2019",
|
||||||
|
|
@ -125,19 +148,60 @@ test('should respect baseurl w/o paths', async ({ runInlineTest }) => {
|
||||||
}`,
|
}`,
|
||||||
'a.test.ts': `
|
'a.test.ts': `
|
||||||
import { foo } from 'foo/b';
|
import { foo } from 'foo/b';
|
||||||
const { test } = pwt;
|
|
||||||
test('test', ({}, testInfo) => {
|
|
||||||
expect(testInfo.project.name).toBe(foo);
|
|
||||||
});
|
|
||||||
`,
|
`,
|
||||||
'foo/b.ts': `
|
'foo/b.ts': `
|
||||||
export const foo: string = 'foo';
|
export const foo = 1;
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.output).not.toContain('Could not');
|
expect(result.exitCode).toBe(1);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.output).toContain(`Cannot find module 'foo/b'`);
|
||||||
expect(result.exitCode).toBe(0);
|
});
|
||||||
|
|
||||||
|
test('should ignore tsconfig with ambiguous paths', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'tsconfig.json': `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2019",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["esnext", "dom", "DOM.Iterable"],
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": { "*/*": ["*/*"] }
|
||||||
|
},
|
||||||
|
}`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { foo } from 'foo/b';
|
||||||
|
`,
|
||||||
|
'foo/b.ts': `
|
||||||
|
export const foo = 1;
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(1);
|
||||||
|
expect(result.output).toContain(`Cannot find module 'foo/b'`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should ignore tsconfig with multi-value paths', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'tsconfig.json': `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2019",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["esnext", "dom", "DOM.Iterable"],
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": { "foo/*": ["./foo/*", "./bar/*"] }
|
||||||
|
},
|
||||||
|
}`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { foo } from 'foo/b';
|
||||||
|
`,
|
||||||
|
'foo/b.ts': `
|
||||||
|
export const foo = 1;
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(1);
|
||||||
|
expect(result.output).toContain(`Cannot find module 'foo/b'`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should respect path resolver in experimental mode', async ({ runInlineTest }) => {
|
test('should respect path resolver in experimental mode', async ({ runInlineTest }) => {
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@
|
||||||
"strictBindCallApply": true,
|
"strictBindCallApply": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"useUnknownInCatchVariables": false,
|
"useUnknownInCatchVariables": false,
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"playwright-core/lib/*": ["../packages/playwright-core/src/*"]
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"include": ["**/*.spec.js", "**/*.ts", "index.d.ts"],
|
"include": ["**/*.spec.js", "**/*.ts", "index.d.ts"],
|
||||||
"exclude": ["playwright-test/"]
|
"exclude": ["playwright-test/"]
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { spawnSync } from 'child_process';
|
import { spawnSync } from 'child_process';
|
||||||
import { PNG } from 'pngjs';
|
import { PNG } from 'pngjs';
|
||||||
import { registry } from 'playwright-core/lib/utils/registry';
|
import { registry } from '../packages/playwright-core/lib/utils/registry';
|
||||||
|
|
||||||
const ffmpeg = registry.findExecutable('ffmpeg')!.executablePath('javascript');
|
const ffmpeg = registry.findExecutable('ffmpeg')!.executablePath('javascript');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
"lib": ["esnext", "dom", "DOM.Iterable"],
|
"lib": ["esnext", "dom", "DOM.Iterable"],
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"*": ["./packages/*/"],
|
|
||||||
"playwright-core/lib/*": ["./packages/playwright-core/src/*"]
|
"playwright-core/lib/*": ["./packages/playwright-core/src/*"]
|
||||||
},
|
},
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue