From e344fe60e79e4764e44991edbc6ba4d5ddce7ef7 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Mon, 22 Aug 2022 21:45:59 +0200 Subject: [PATCH] fix(pwt): compatibility in CWD with wrong casing (#16636) --- packages/playwright-test/src/loader.ts | 22 ++++++++++ tests/installation/npmTest.ts | 4 +- ...should-handle-incorrect-cwd-casing.spec.ts | 41 +++++++++++++++++++ tests/installation/playwright.config.ts | 2 +- 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 tests/installation/playwright-test-should-handle-incorrect-cwd-casing.spec.ts diff --git a/packages/playwright-test/src/loader.ts b/packages/playwright-test/src/loader.ts index 622a4dd8d6747..22cdeda0587d2 100644 --- a/packages/playwright-test/src/loader.ts +++ b/packages/playwright-test/src/loader.ts @@ -293,6 +293,8 @@ export class Loader { } private async _requireOrImport(file: string) { + if (process.platform === 'win32') + file = await fixWin32FilepathCapitalization(file); const revertBabelRequire = installTransform(); const isModule = fileIsModule(file); try { @@ -678,3 +680,23 @@ export function folderIsModule(folder: string): boolean { // Rely on `require` internal caching logic. return require(packageJsonPath).type === 'module'; } + +async function fixWin32FilepathCapitalization(file: string): Promise { + /** + * On Windows with PowerShell <= 6 it is possible to have a CWD with different + * casing than what the actual directory on the filesystem is. This can cause + * that we require the file multiple times with different casing. To mitigate + * this we get the actual underlying filesystem path and use that. + * https://github.com/microsoft/playwright/issues/9193#issuecomment-1219362150 + */ + const realFile = await new Promise((resolve, reject) => fs.realpath.native(file, (error, realFile) => { + if (error) + return reject(error); + resolve(realFile); + })); + // We do not want to resolve them (e.g. 8.3 filenames), so we do a best effort + // approach by only using it if the actual lowercase characters are the same: + if (realFile.toLowerCase() === file.toLowerCase()) + return realFile; + return file; +} diff --git a/tests/installation/npmTest.ts b/tests/installation/npmTest.ts index 86e8a9eeaa093..7e086c1071c0f 100644 --- a/tests/installation/npmTest.ts +++ b/tests/installation/npmTest.ts @@ -25,8 +25,8 @@ import debugLogger from 'debug'; import { Registry } from './registry'; import { spawnAsync } from './spawnAsync'; - -export const TMP_WORKSPACES = path.join(os.platform() === 'darwin' ? '/tmp' : os.tmpdir(), 'pwt', 'workspaces'); +// os.tmpdir() on Windows returns a 8.3 filename, so we resolve it. +export const TMP_WORKSPACES = path.join(os.platform() === 'darwin' ? '/tmp' : fs.realpathSync.native(os.tmpdir()), 'pwt', 'workspaces'); const debug = debugLogger('itest'); diff --git a/tests/installation/playwright-test-should-handle-incorrect-cwd-casing.spec.ts b/tests/installation/playwright-test-should-handle-incorrect-cwd-casing.spec.ts new file mode 100644 index 0000000000000..6099097191b4a --- /dev/null +++ b/tests/installation/playwright-test-should-handle-incorrect-cwd-casing.spec.ts @@ -0,0 +1,41 @@ +/** + * 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 fs from 'fs'; +import path from 'path'; +import { test, expect } from './npmTest'; + +test('@playwright/test should handle incorrect cwd casing', async ({ exec, tmpWorkspace }) => { + test.skip(process.platform !== 'win32'); + const cwd = path.join(tmpWorkspace, 'expectedcasing'); + fs.mkdirSync(cwd); + fs.writeFileSync(path.join(cwd, 'sample.spec.ts'), ` + import { test, expect } from '@playwright/test'; + test('should pass', async () => { + expect(1 + 1).toBe(2); + }) + `); + fs.writeFileSync(path.join(cwd, 'sample.spec.js'), ` + const { test, expect } = require('@playwright/test'); + test('should pass', async () => { + expect(1 + 1).toBe(2); + }) + `); + await exec('npm init -y', { cwd }); + await exec('npm i --foreground-scripts @playwright/test', { cwd }); + + const output = await exec('npx playwright test --reporter=list', { cwd: path.join(tmpWorkspace, 'eXpEcTeDcAsInG') }); + expect(output).toContain('2 passed'); +}); diff --git a/tests/installation/playwright.config.ts b/tests/installation/playwright.config.ts index 712fa633dc228..c0f553e44ba7f 100644 --- a/tests/installation/playwright.config.ts +++ b/tests/installation/playwright.config.ts @@ -27,7 +27,7 @@ const config: PlaywrightTestConfig = { timeout: 5 * 60 * 1000, retries: 0, reporter: process.env.CI ? [ - ['dot'], + ['list'], ['json', { outputFile: path.join(outputDir, 'report.json') }], ] : [['list'], ['html', { open: 'on-failure' }]], forbidOnly: !!process.env.CI,