1414 * limitations under the License.
1515 */
1616
17+ import fs from 'fs' ;
18+ import path from 'path' ;
19+
1720import { test , expect } from './playwright-test-fixtures' ;
1821
1922test ( 'should print tsconfig parsing error' , async ( { runInlineTest } ) => {
@@ -622,41 +625,100 @@ test('should resolve extends from an explicit node_modules subpath', async ({ ru
622625 expect ( result . exitCode ) . toBe ( 0 ) ;
623626} ) ;
624627
625- test ( 'should fail loudly when extends path cannot be resolved' , async ( { runInlineTest } ) => {
626- test . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/41543' } ) ;
628+ test ( 'should ignore extends bare specifier resolvable only via node_modules walk-up' , async ( { runInlineTest } , testInfo ) => {
629+ test . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/41989' } ) ;
630+
631+ const baseDir = testInfo . outputPath ( ) ;
632+ const symlinkType = process . platform === 'win32' ? 'junction' : 'dir' ;
633+ // npm symlinks "file:" dependencies into node_modules. The realpath escapes node_modules,
634+ // so the dependency goes through per-file tsconfig discovery.
635+ await fs . promises . mkdir ( path . join ( baseDir , 'node_modules' ) , { recursive : true } ) ;
636+ await fs . promises . symlink ( path . join ( baseDir , 'dep-src' ) , path . join ( baseDir , 'node_modules' , 'dep' ) , symlinkType ) ;
627637
628638 const result = await runInlineTest ( {
629- 'tsconfig.json' : `{
630- "extends": "./tsconfig.bas.json",
631- }` ,
639+ 'package.json' : JSON . stringify ( { name : 'repro' , private : true , dependencies : { 'dep' : 'file:./dep-src' , 'foo' : '1.0.0' } } ) ,
640+ // "foo" is hoisted to the root node_modules and is absent from dep's own node_modules.
641+ // tsc resolves the "extends" below by walking up node_modules from dep-src.
642+ 'node_modules/foo/package.json' : JSON . stringify ( { name : 'foo' , version : '1.0.0' , main : 'index.js' } ) ,
643+ 'node_modules/foo/index.js' : `module.exports = 'foo';` ,
644+ 'node_modules/foo/tsconfig.json' : `{ "compilerOptions": {} }` ,
645+ 'dep-src/package.json' : JSON . stringify ( { name : 'dep' , version : '1.0.0' , main : 'index.js' } ) ,
646+ 'dep-src/tsconfig.json' : `{ "extends": "foo/tsconfig.json" }` ,
647+ 'dep-src/index.js' : `
648+ require('foo');
649+ module.exports = 'dep';
650+ ` ,
651+ 'a.test.ts' : `
652+ import { test, expect } from '@playwright/test';
653+ const dep = require('dep');
654+ test('test', () => {
655+ expect(dep).toBe('dep');
656+ });
657+ ` ,
658+ } ) ;
659+
660+ expect ( result . passed ) . toBe ( 1 ) ;
661+ expect ( result . exitCode ) . toBe ( 0 ) ;
662+ } ) ;
663+
664+ test ( 'should ignore extends subpath resolvable only via package.json exports' , async ( { runInlineTest } ) => {
665+ test . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/41989' } ) ;
666+
667+ const result = await runInlineTest ( {
668+ 'package.json' : JSON . stringify ( { name : 'test-project' } ) ,
669+ // The "./next" subpath only exists in the exports map, there is no "next.json" file.
670+ 'node_modules/@repo/tsconfig/package.json' : JSON . stringify ( { name : '@repo/tsconfig' , version : '1.0.0' , exports : { './next' : './tsconfig.next.json' } } ) ,
671+ 'node_modules/@repo/tsconfig/tsconfig.next.json' : `{ "compilerOptions": {} }` ,
672+ 'tsconfig.json' : `{ "extends": "@repo/tsconfig/next" }` ,
632673 'a.test.ts' : `
633674 import { test, expect } from '@playwright/test';
634675 test('test', () => {});
635676 ` ,
636677 } ) ;
637678
638- expect ( result . exitCode ) . toBe ( 1 ) ;
639- expect ( result . output ) . toContain ( 'Failed to resolve "extends" path "./tsconfig.bas.json"' ) ;
679+ expect ( result . passed ) . toBe ( 1 ) ;
680+ expect ( result . exitCode ) . toBe ( 0 ) ;
640681} ) ;
641682
642- test ( 'should fail loudly when references path cannot be resolved ' , async ( { runInlineTest } ) => {
643- test . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/41543 ' } ) ;
683+ test ( 'should ignore extends bare package name resolvable only via package.json exports ' , async ( { runInlineTest } ) => {
684+ test . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/41989 ' } ) ;
644685
645686 const result = await runInlineTest ( {
687+ 'package.json' : JSON . stringify ( { name : 'test-project' } ) ,
688+ 'node_modules/typescript-config-silverwind/package.json' : JSON . stringify ( { name : 'typescript-config-silverwind' , version : '1.0.0' , exports : './tsconfig.json' } ) ,
689+ 'node_modules/typescript-config-silverwind/tsconfig.json' : `{ "compilerOptions": {} }` ,
690+ 'tsconfig.json' : `{ "extends": "typescript-config-silverwind" }` ,
691+ 'a.test.ts' : `
692+ import { test, expect } from '@playwright/test';
693+ test('test', () => {});
694+ ` ,
695+ } ) ;
696+
697+ expect ( result . passed ) . toBe ( 1 ) ;
698+ expect ( result . exitCode ) . toBe ( 0 ) ;
699+ } ) ;
700+
701+ test ( 'should ignore tsconfig references pointing to a directory' , async ( { runInlineTest } ) => {
702+ test . info ( ) . annotations . push ( { type : 'issue' , description : 'https://github.com/microsoft/playwright/issues/41998' } ) ;
703+
704+ const result = await runInlineTest ( {
705+ // tsc resolves a directory-form reference to "./sub/tsconfig.json",
706+ // as written by "tsc --build" and "nx sync".
646707 'tsconfig.json' : `{
647- "files": [],
648- "references": [
649- { "path": "./tsconfig.doesnotexist.json" }
650- ]
708+ "compilerOptions": { "composite": true },
709+ "references": [{ "path": "./sub" }],
710+ }` ,
711+ 'sub/tsconfig.json' : `{
712+ "compilerOptions": { "composite": true },
651713 }` ,
652714 'a.test.ts' : `
653715 import { test, expect } from '@playwright/test';
654716 test('test', () => {});
655717 ` ,
656718 } ) ;
657719
658- expect ( result . exitCode ) . toBe ( 1 ) ;
659- expect ( result . output ) . toContain ( 'Failed to resolve "references" path "./tsconfig.doesnotexist.json"' ) ;
720+ expect ( result . passed ) . toBe ( 1 ) ;
721+ expect ( result . exitCode ) . toBe ( 0 ) ;
660722} ) ;
661723
662724test ( 'should respect tsconfig project references' , async ( { runInlineTest } ) => {
@@ -671,9 +733,6 @@ test('should respect tsconfig project references', async ({ runInlineTest }) =>
671733 { "path": "./tsconfig.test.json" }
672734 ]
673735 }` ,
674- 'tsconfig.app.json' : `{
675- "compilerOptions": {},
676- }` ,
677736 'tsconfig.test.json' : `{
678737 "compilerOptions": {
679738 "baseUrl": ".",
0 commit comments