Skip to content

test: Refactor playwright bundle generation #8029

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
*.md
.nxcache
packages/browser-integration-tests/fixtures
1 change: 1 addition & 0 deletions packages/browser-integration-tests/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
'loader-suites/**/subject.js',
'scripts/**',
'fixtures/**',
'tmp/**',
],
parserOptions: {
sourceType: 'module',
Expand Down
1 change: 1 addition & 0 deletions packages/browser-integration-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
test-results
tmp
2 changes: 2 additions & 0 deletions packages/browser-integration-tests/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tmp
fixtures
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'fs';
import path from 'path';

import { sentryTest, TEST_HOST } from '../../../../utils/fixtures';
import { LOADER_CONFIGS } from '../../../../utils/generatePage';
import { LOADER_CONFIGS } from '../../../../utils/generatePlugin';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../utils/helpers';

const bundle = process.env.PW_BUNDLE || '';
Expand Down
3 changes: 2 additions & 1 deletion packages/browser-integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"private": true,
"scripts": {
"clean": "rimraf -g suites/**/dist loader-suites/**/dist",
"clean": "rimraf -g suites/**/dist loader-suites/**/dist tmp",
"install-browsers": "playwright install --with-deps",
"lint": "run-s lint:prettier lint:eslint",
"lint:eslint": "eslint . --format stylish",
Expand Down Expand Up @@ -58,6 +58,7 @@
},
"devDependencies": {
"@types/glob": "8.0.0",
"@types/node": "^14.6.4",
"glob": "8.0.3"
},
"volta": {
Expand Down
3 changes: 3 additions & 0 deletions packages/browser-integration-tests/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ const config: PlaywrightTestConfig = {
// Use 3 workers on CI, else use defaults (based on available CPU cores)
// Note that 3 is a random number selected to work well with our CI setup
workers: process.env.CI ? 3 : undefined,

globalSetup: require.resolve('./playwright.setup.ts'),
};

export default config;
5 changes: 5 additions & 0 deletions packages/browser-integration-tests/playwright.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import setupStaticAssets from './utils/staticAssets';

export default function globalSetup(): Promise<void> {
return setupStaticAssets();
}
3 changes: 1 addition & 2 deletions packages/browser-integration-tests/utils/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { test as base } from '@playwright/test';
import fs from 'fs';
import path from 'path';

import { generateLoader, generatePage } from './generatePage';
import { generatePage } from './generatePage';

export const TEST_HOST = 'http://sentry-test.io';

Expand Down Expand Up @@ -53,7 +53,6 @@ const sentryTest = base.extend<TestFixtures>({
const pagePath = `${TEST_HOST}/index.html`;

await build(testDir);
generateLoader(testDir);

// Serve all assets under
if (!skipRouteHandler) {
Expand Down
82 changes: 2 additions & 80 deletions packages/browser-integration-tests/utils/generatePage.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,10 @@
import { existsSync, mkdirSync, readFileSync, symlinkSync, unlinkSync, writeFileSync } from 'fs';
import { existsSync, mkdirSync } from 'fs';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import path from 'path';
import webpack from 'webpack';

import webpackConfig from '../webpack.config';
import { TEST_HOST } from './fixtures';
import SentryScenarioGenerationPlugin from './generatePlugin';

const LOADER_TEMPLATE = readFileSync(path.join(__dirname, '../fixtures/loader.js'), 'utf-8');

export const LOADER_CONFIGS: Record<string, { bundle: string; options: Record<string, unknown>; lazy: boolean }> = {
loader_base: {
bundle: 'browser/build/bundles/bundle.es5.min.js',
options: {},
lazy: true,
},
loader_eager: {
bundle: 'browser/build/bundles/bundle.es5.min.js',
options: {},
lazy: false,
},
loader_debug: {
bundle: 'browser/build/bundles/bundle.es5.debug.min.js',
options: { debug: true },
lazy: true,
},
loader_tracing: {
bundle: 'browser/build/bundles/bundle.tracing.es5.min.js',
options: { tracesSampleRate: 1 },
lazy: false,
},
loader_replay: {
bundle: 'browser/build/bundles/bundle.replay.min.js',
options: { replaysSessionSampleRate: 1, replaysOnErrorSampleRate: 1 },
lazy: false,
},
loader_tracing_replay: {
bundle: 'browser/build/bundles/bundle.tracing.replay.debug.min.js',
options: { tracesSampleRate: 1, replaysSessionSampleRate: 1, replaysOnErrorSampleRate: 1, debug: true },
lazy: false,
},
};

const bundleKey = process.env.PW_BUNDLE || '';

export function generateLoader(outPath: string): void {
const localPath = `${outPath}/dist`;

if (!existsSync(localPath)) {
return;
}

// Generate loader files
const loaderConfig = LOADER_CONFIGS[bundleKey];

if (!loaderConfig) {
throw new Error(`Unknown loader bundle key: ${bundleKey}`);
}

const localCdnBundlePath = path.join(localPath, 'cdn.bundle.js');

try {
unlinkSync(localCdnBundlePath);
} catch {
// ignore if this didn't exist
}

const cdnSourcePath = path.resolve(__dirname, `../../${loaderConfig.bundle}`);
symlinkSync(cdnSourcePath, localCdnBundlePath);

const loaderPath = path.join(localPath, 'loader.js');
const loaderContent = LOADER_TEMPLATE.replace('__LOADER_BUNDLE__', `'${TEST_HOST}/cdn.bundle.js'`)
.replace(
'__LOADER_OPTIONS__',
JSON.stringify({
dsn: 'https://[email protected]/1337',
...loaderConfig.options,
}),
)
.replace('__LOADER_LAZY__', loaderConfig.lazy ? 'true' : 'false');

writeFileSync(loaderPath, loaderContent, 'utf-8');
}

export async function generatePage(
initPath: string,
subjectPath: string,
Expand Down Expand Up @@ -110,7 +32,7 @@ export async function generatePage(
filename: '[name].bundle.js',
},
plugins: [
new SentryScenarioGenerationPlugin(),
new SentryScenarioGenerationPlugin(localPath),
new HtmlWebpackPlugin({
filename: outPageName,
template: templatePath,
Expand Down
98 changes: 83 additions & 15 deletions packages/browser-integration-tests/utils/generatePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import HtmlWebpackPlugin, { createHtmlTagObject } from 'html-webpack-plugin';
import path from 'path';
import type { Compiler } from 'webpack';

import { addStaticAsset, addStaticAssetSymlink } from './staticAssets';

const LOADER_TEMPLATE = fs.readFileSync(path.join(__dirname, '../fixtures/loader.js'), 'utf-8');
const PACKAGES_DIR = '../../packages';

/**
Expand Down Expand Up @@ -34,6 +37,12 @@ const BUNDLE_PATHS: Record<string, Record<string, string>> = {
bundle_tracing_es6_min: 'build/bundles/bundle.tracing.min.js',
bundle_tracing_replay_es6: 'build/bundles/bundle.tracing.replay.js',
bundle_tracing_replay_es6_min: 'build/bundles/bundle.tracing.replay.min.js',
loader_base: 'build/bundles/bundle.es5.min.js',
loader_eager: 'build/bundles/bundle.es5.min.js',
loader_debug: 'build/bundles/bundle.es5.debug.min.js',
loader_tracing: 'build/bundles/bundle.tracing.es5.min.js',
loader_replay: 'build/bundles/bundle.replay.min.js',
loader_tracing_replay: 'build/bundles/bundle.tracing.replay.debug.min.js',
},
integrations: {
cjs: 'build/npm/cjs/index.js',
Expand All @@ -51,6 +60,33 @@ const BUNDLE_PATHS: Record<string, Record<string, string>> = {
},
};

export const LOADER_CONFIGS: Record<string, { options: Record<string, unknown>; lazy: boolean }> = {
loader_base: {
options: {},
lazy: true,
},
loader_eager: {
options: {},
lazy: false,
},
loader_debug: {
options: { debug: true },
lazy: true,
},
loader_tracing: {
options: { tracesSampleRate: 1 },
lazy: false,
},
loader_replay: {
options: { replaysSessionSampleRate: 1, replaysOnErrorSampleRate: 1 },
lazy: false,
},
loader_tracing_replay: {
options: { tracesSampleRate: 1, replaysSessionSampleRate: 1, replaysOnErrorSampleRate: 1, debug: true },
lazy: false,
},
};

/*
* Generate webpack aliases based on packages in monorepo
*
Expand Down Expand Up @@ -97,9 +133,14 @@ function generateSentryAlias(): Record<string, string> {
class SentryScenarioGenerationPlugin {
public requiredIntegrations: string[] = [];
public requiresWASMIntegration: boolean = false;
public localOutPath: string;

private _name: string = 'SentryScenarioGenerationPlugin';

public constructor(localOutPath: string) {
this.localOutPath = localOutPath;
}

public apply(compiler: Compiler): void {
compiler.options.resolve.alias = generateSentryAlias();
compiler.options.externals = useBundleOrLoader
Expand Down Expand Up @@ -136,20 +177,34 @@ class SentryScenarioGenerationPlugin {
const bundleName = 'browser';
const bundlePath = BUNDLE_PATHS[bundleName][bundleKey];

let bundleObject =
bundlePath &&
createHtmlTagObject('script', {
src: path.resolve(PACKAGES_DIR, bundleName, bundlePath),
});

if (!bundleObject && useLoader) {
bundleObject = createHtmlTagObject('script', {
src: 'loader.js',
});
if (!bundlePath) {
throw new Error(`Could not find bundle or loader for key ${bundleKey}`);
}

if (!bundleObject) {
throw new Error(`Could not find bundle or loader for key ${bundleKey}`);
const bundleObject = useLoader
? createHtmlTagObject('script', {
src: 'loader.js',
})
: createHtmlTagObject('script', {
src: 'cdn.bundle.js',
});

addStaticAssetSymlink(this.localOutPath, path.resolve(PACKAGES_DIR, bundleName, bundlePath), 'cdn.bundle.js');

if (useLoader) {
const loaderConfig = LOADER_CONFIGS[bundleKey];

addStaticAsset(this.localOutPath, 'loader.js', () => {
return LOADER_TEMPLATE.replace('__LOADER_BUNDLE__', "'/cdn.bundle.js'")
.replace(
'__LOADER_OPTIONS__',
JSON.stringify({
dsn: 'https://[email protected]/1337',
...loaderConfig.options,
}),
)
.replace('__LOADER_LAZY__', loaderConfig.lazy ? 'true' : 'false');
});
}

// Convert e.g. bundle_tracing_es5_min to bundle_es5_min
Expand All @@ -159,20 +214,33 @@ class SentryScenarioGenerationPlugin {
.replace('_tracing', '');

this.requiredIntegrations.forEach(integration => {
const integrationObject = createHtmlTagObject('script', {
src: path.resolve(
const fileName = `${integration}.bundle.js`;
addStaticAssetSymlink(
this.localOutPath,
path.resolve(
PACKAGES_DIR,
'integrations',
BUNDLE_PATHS['integrations'][integrationBundleKey].replace('[INTEGRATION_NAME]', integration),
),
fileName,
);

const integrationObject = createHtmlTagObject('script', {
src: fileName,
});

data.assetTags.scripts.unshift(integrationObject);
});

if (this.requiresWASMIntegration && BUNDLE_PATHS['wasm'][integrationBundleKey]) {
addStaticAssetSymlink(
this.localOutPath,
path.resolve(PACKAGES_DIR, 'wasm', BUNDLE_PATHS['wasm'][integrationBundleKey]),
'wasm.bundle.js',
);

const wasmObject = createHtmlTagObject('script', {
src: path.resolve(PACKAGES_DIR, 'wasm', BUNDLE_PATHS['wasm'][integrationBundleKey]),
src: 'wasm.bundle.js',
});

data.assetTags.scripts.unshift(wasmObject);
Expand Down
51 changes: 51 additions & 0 deletions packages/browser-integration-tests/utils/staticAssets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import fs from 'fs';
import path from 'path';

export const STATIC_DIR = path.join(__dirname, '../tmp/static');

export default async function setupStaticAssets(): Promise<void> {
if (fs.existsSync(STATIC_DIR)) {
await fs.promises.rm(STATIC_DIR, { recursive: true });
}

await fs.promises.mkdir(STATIC_DIR, { recursive: true });
}

export function addStaticAsset(localOutPath: string, fileName: string, cb: () => string): void {
const newPath = path.join(STATIC_DIR, fileName);

// Only copy files once
if (!fs.existsSync(newPath)) {
fs.writeFileSync(newPath, cb(), 'utf-8');
}

symlinkAsset(newPath, path.join(localOutPath, fileName));
}

export function addStaticAssetSymlink(localOutPath: string, originalPath: string, fileName: string): void {
const newPath = path.join(STATIC_DIR, fileName);

// Only copy files once
if (!fs.existsSync(newPath)) {
fs.symlinkSync(originalPath, newPath);
}

symlinkAsset(newPath, path.join(localOutPath, fileName));
}

function symlinkAsset(originalPath: string, targetPath: string): void {
try {
fs.unlinkSync(targetPath);
} catch {
// ignore errors here
}

try {
fs.linkSync(originalPath, targetPath);
} catch (error) {
// only ignore these kind of errors
if (!`${error}`.includes('file already exists')) {
throw error;
}
}
}