Skip to content

Commit 5594239

Browse files
authored
fix: add cjs sauce.config and load correct type based on project (#218)
* Add a cjs version of wrapper config * copy both configs to build dir * Try to load the correct config wrapper Check package.json for "type" and if its explicitly "module" load up sauce.config.mjs. Then try checking the customer's config, if its suffixed with '.mjs' or '.mts' then also load up sauce.config.mjs. Otherwise, load the commonjs version. * Only need to check package.json for module type I don't think its possible or sensical to have type: "module" but a playwright.config.cts file. * Update sauce.config.cjs to have changes since it was moved to .mjs * A little safer package.json access * Update jsdoc for isEsmProject * Read and parse package.json instead of importing * remove unnecessary string conversion * Add an integration test for commonjs + typescript * remove unnecessary files * projectPath should always be set, but be defensive anyways Log it if it should ever come to pass.
1 parent b11e84b commit 5594239

14 files changed

+172
-27
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
],
1010
"main": "lib/playwright-recorder.js",
1111
"scripts": {
12-
"build": "tsc && cp src/sauce.config.mjs lib/",
12+
"build": "tsc && cp src/sauce.config.* lib/",
1313
"clean": "rm -rf lib",
1414
"lint": "eslint tests/ src/",
1515
"test": "./bin/wait_display && DISPLAY=\"$(cat DISPLAY)\" node .",

src/playwright-runner.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,12 @@ async function runPlaywright(nodeBin: string, runCfg: RunnerConfig): Promise<Run
210210

211211
// Copy our runner's playwright config to a custom location in order to
212212
// preserve the customer's config which we may want to load in the future
213-
const configFile = path.join(runCfg.projectPath, 'sauce.config.mjs');
214-
fs.copyFileSync(path.join(__dirname, 'sauce.config.mjs'), configFile);
213+
const configFileName =
214+
await utils.isEsmProject(runCfg.projectPath) ?
215+
'sauce.config.mjs' :
216+
'sauce.config.cjs';
217+
const configFile = path.join(runCfg.projectPath, configFileName);
218+
fs.copyFileSync(path.join(__dirname, configFileName), configFile);
215219

216220
const defaultArgs = {
217221
output: runCfg.playwrightOutputFolder,

src/sauce.config.cjs

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// @ts-check
2+
const process = require('process');
3+
const _ = require('lodash');
4+
const fs = require('fs');
5+
6+
let userConfig = {};
7+
8+
// Prefer ts over js to match default behaviour of playwright-test
9+
const configFiles = process.env.PLAYWRIGHT_CFG_FILE ?
10+
[process.env.PLAYWRIGHT_CFG_FILE] :
11+
['./playwright.config.ts', './playwright.config.js'];
12+
13+
for (const file of configFiles) {
14+
if (fs.existsSync(file)) {
15+
try {
16+
userConfig = require(file);
17+
// it should put config just under root level to get it work with playwright.config.ts
18+
// there is no such issue with playwright.config.js
19+
if (userConfig.default) {
20+
userConfig = userConfig.default;
21+
}
22+
break;
23+
} catch (e) {
24+
console.error(e);
25+
}
26+
}
27+
}
28+
29+
const overrides = {
30+
use: {
31+
headless: process.env.HEADLESS === 'true',
32+
video: 'off',
33+
launchOptions: {},
34+
},
35+
reporter: [
36+
['list'],
37+
// outputFile is set by playwright-runner.js as an env variable. The runner needs to process it
38+
// so better for it to set the output path
39+
['junit'],
40+
// outputFile is set by playwright-runner.js as an env variable. The runner needs to process it
41+
// so better for it to set the output path
42+
['@saucelabs/playwright-reporter',
43+
{
44+
upload: false,
45+
},
46+
],
47+
],
48+
testIgnore: process.env.TEST_IGNORE?.split(','),
49+
};
50+
51+
// Values that are arrays are merged at the very end (see arrMerger()), but primitives are not.
52+
// Allow the user to set a single reporter like so: `reporter: 'list'`.
53+
if (userConfig.reporter && !(userConfig.reporter instanceof Array)) {
54+
overrides.reporter.push([userConfig.reporter]);
55+
}
56+
57+
if (process.env.BROWSER_NAME !== 'chrome') {
58+
// chromium, firefox and webkit come pre-packaged with playwright.
59+
// So we can just pass those browser values to playwright and
60+
// it knows what to do and where to pick them up.
61+
overrides.use.browserName = process.env.BROWSER_NAME; // override browserName with suite browserName
62+
} else {
63+
// Google chrome is provided by the sauce VM. So we have to let playwright know where to look.
64+
overrides.use.channel = 'chrome';
65+
overrides.use.launchOptions.executablePath = process.env.BROWSER_PATH;
66+
}
67+
68+
if ('HTTP_PROXY' in process.env && process.env.HTTP_PROXY !== '') {
69+
const proxy = {
70+
server: process.env.HTTP_PROXY,
71+
};
72+
73+
overrides.use.contextOptions = {proxy, ignoreHTTPSErrors: true};
74+
// Need to set the browser launch option as well, it is a hard requirement when testing chromium + windows.
75+
overrides.use.launchOptions = {proxy, ignoreHTTPSErrors: true};
76+
}
77+
78+
function arrMerger(objValue, srcValue) {
79+
if (_.isArray(objValue)) {
80+
return objValue.concat(srcValue);
81+
}
82+
}
83+
84+
module.exports = _.mergeWith(userConfig, overrides, arrMerger);

src/utils.ts

+28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as path from 'node:path';
2+
import { readFile } from 'node:fs/promises';
23

34
import shell from 'shelljs';
45
import logger from '@wdio/logger';
@@ -80,3 +81,30 @@ export function setEnvironmentVariables (envVars: Record<string, string> = {}) {
8081
process.env[key] = value;
8182
}
8283
}
84+
85+
/**
86+
* Check project's module type from an included package.json and return true
87+
* if configured for ESM.
88+
*/
89+
export async function isEsmProject(projectPath?: string) {
90+
if (!projectPath) {
91+
console.warn('Project path expected, but is undefined');
92+
return false
93+
}
94+
95+
const packagePath = path.join(projectPath, 'package.json');
96+
let packageJson: unknown;
97+
try {
98+
const contents = await readFile(packagePath, { encoding: 'utf-8' });
99+
packageJson = JSON.parse(contents);
100+
} catch {
101+
return false
102+
}
103+
104+
return (
105+
packageJson &&
106+
typeof packageJson === 'object' &&
107+
'type' in packageJson &&
108+
packageJson.type === 'module'
109+
);
110+
}

tests/fixtures/local/basic-ts/.sauce/config.yml

-22
This file was deleted.
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "commonjs"
3+
}

tests/fixtures/local/basic-ts/sauce-runner.json renamed to tests/fixtures/local/ts-cjs/sauce-runner.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
},
55
"suites": [
66
{
7-
"name": "basic-ts",
7+
"name": "ts-cjs",
88
"param": {
99
"browserName": "chromium",
1010
"headless": true

tests/fixtures/local/basic-ts/tests/test.spec.ts renamed to tests/fixtures/local/ts-cjs/tests/test.spec.ts

+2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
* limitations under the License.
1515
*/
1616
import { test, expect } from '@playwright/test';
17+
import config from '../playwright.config.js';
1718

1819
test('is a basic test with the page', async ({ page }) => {
1920
await page.goto('https://playwright.dev/');
2021
expect(await page.innerText('.navbar__title')).toBe('Playwright');
22+
expect(config.use?.video).toEqual('off');
2123
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from '@playwright/test';
2+
3+
export default defineConfig({
4+
use: {
5+
headless: true,
6+
video: 'on',
7+
},
8+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"playwright": {
3+
"version": "1.12.3"
4+
},
5+
"suites": [
6+
{
7+
"name": "ts-esm",
8+
"param": {
9+
"browserName": "chromium",
10+
"headless": true
11+
},
12+
"testMatch": ".*.spec.ts"
13+
}
14+
]
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Copyright Microsoft Corporation. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { test, expect } from '@playwright/test';
17+
import config from '../playwright.config.js';
18+
19+
test('is a basic test with the page', async ({ page }) => {
20+
await page.goto('https://playwright.dev/');
21+
expect(await page.innerText('.navbar__title')).toBe('Playwright');
22+
expect(config.use?.video).toEqual('off');
23+
});

tests/run.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env bash
22

33
# suite=result
4-
tests=(basic-js=success basic-ts=success broken-tests=failure config-merging=success)
4+
tests=(basic-js=success broken-tests=failure config-merging=success ts-esm=success ts-cjs=success)
55

66
for i in ${tests[@]}; do
77
key=$(echo ${i} | cut -d '=' -f 1)

0 commit comments

Comments
 (0)