Skip to content

Commit e9a41c7

Browse files
committed
chore(itest): guard playwright tarball size and bundled deps
Adds two installation tests: one asserts upper bounds on the packed playwright-core and playwright tarball sizes, the other asserts lower bounds on the number of inlined npm packages per bundle LICENSE, parsed from the "Total Packages" summary line.
1 parent 3912da7 commit e9a41c7

2 files changed

Lines changed: 109 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
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 fs from 'fs';
17+
import path from 'path';
18+
import { test, expect } from './npmTest';
19+
20+
// Lower bounds on the number of inlined npm packages per LICENSE. If a bundle drops below
21+
// these, something is broken with bundle generation or dependencies were silently removed.
22+
const EXPECTED: Record<string, Record<string, number>> = {
23+
'playwright-core': {
24+
'lib/coreBundle.js.LICENSE': 45,
25+
'lib/utilsBundle.js.LICENSE': 80,
26+
},
27+
'playwright': {
28+
'lib/matchers/expect.js.LICENSE': 30,
29+
'lib/transform/esmLoader.js.LICENSE': 10,
30+
'lib/transform/babelBundle.js.LICENSE': 65,
31+
},
32+
};
33+
34+
async function collectLicenses(dir: string): Promise<string[]> {
35+
const result: string[] = [];
36+
const walk = async (d: string) => {
37+
const entries = await fs.promises.readdir(d, { withFileTypes: true }).catch(() => []);
38+
for (const e of entries) {
39+
const p = path.join(d, e.name);
40+
if (e.isDirectory())
41+
await walk(p);
42+
else if (e.name.endsWith('.LICENSE'))
43+
result.push(p);
44+
}
45+
};
46+
await walk(dir);
47+
return result.sort();
48+
}
49+
50+
for (const [pkg, licenses] of Object.entries(EXPECTED)) {
51+
test(`${pkg} bundles ship .LICENSE files with expected package counts`, async ({ exec, tmpWorkspace }) => {
52+
const registry = JSON.parse(await fs.promises.readFile(path.join(__dirname, '.registry.json'), 'utf8'));
53+
const tarball: string = registry[pkg];
54+
expect(tarball, `no tarball recorded for ${pkg} in .registry.json`).toBeTruthy();
55+
56+
const extractDir = path.join(tmpWorkspace, `extract-${pkg}`);
57+
await fs.promises.mkdir(extractDir, { recursive: true });
58+
await exec('tar', '-xzf', tarball, '-C', extractDir);
59+
60+
const libDir = path.join(extractDir, 'package', 'lib');
61+
const found = await collectLicenses(libDir);
62+
const expected = Object.keys(licenses).map(p => path.join(libDir, ...p.slice('lib/'.length).split('/'))).sort();
63+
expect(found, `LICENSE files under ${pkg}/lib do not match the expected set — update EXPECTED`).toEqual(expected);
64+
65+
for (const [relPath, minPackages] of Object.entries(licenses)) {
66+
const absPath = path.join(extractDir, 'package', relPath);
67+
const contents = await fs.promises.readFile(absPath, 'utf8');
68+
const match = contents.match(/^Total Packages: (\d+)$/m);
69+
expect(match, `${pkg}/${relPath} is missing the "Total Packages" summary line`).toBeTruthy();
70+
const count = Number(match![1]);
71+
test.info().annotations.push({ type: 'licenses', description: `${pkg}/${relPath}: ${count} packages` });
72+
expect(count, `${pkg}/${relPath} lists only ${count} packages, expected at least ${minPackages}`).toBeGreaterThanOrEqual(minPackages);
73+
}
74+
});
75+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
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 fs from 'fs';
17+
import path from 'path';
18+
import { test, expect } from './npmTest';
19+
20+
const THRESHOLDS: Record<string, number> = {
21+
'playwright-core': 7 * 1024 * 1024,
22+
'playwright': 3 * 1024 * 1024,
23+
};
24+
25+
for (const [pkg, maxBytes] of Object.entries(THRESHOLDS)) {
26+
test(`${pkg} tarball stays under ${(maxBytes / 1024 / 1024).toFixed(2)} MB`, async () => {
27+
const registry = JSON.parse(await fs.promises.readFile(path.join(__dirname, '.registry.json'), 'utf8'));
28+
const tarball: string = registry[pkg];
29+
expect(tarball, `no tarball recorded for ${pkg} in .registry.json`).toBeTruthy();
30+
const { size } = await fs.promises.stat(tarball);
31+
test.info().annotations.push({ type: 'size', description: `${pkg}: ${size} bytes` });
32+
expect(size, `${pkg} tarball ${tarball} is ${size} bytes, limit ${maxBytes}`).toBeLessThanOrEqual(maxBytes);
33+
});
34+
}

0 commit comments

Comments
 (0)