Skip to content

Commit

Permalink
test(esbuild-meta): add e2e test to pipeline (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherPHolder committed Jul 4, 2024
2 parents 3bc335f + 08d5e1f commit d115914
Show file tree
Hide file tree
Showing 20 changed files with 368 additions and 77 deletions.
118 changes: 57 additions & 61 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,35 +73,34 @@ jobs:
- name: Lint Affected
run: npx nx affected --target=lint --parallel=3

# Commented out because its hanging
# test:
# name: Test Affected
# needs: setup
# runs-on: ubuntu-latest
#
# steps:
# - uses: actions/checkout@v4
# with:
# # We need to fetch all branches and commits so that Nx affected has a base to compare against.
# fetch-depth: 0
# # Derive appropriate SHAs for base and head for `nx affected` commands
# - uses: nrwl/nx-set-shas@v3
# - uses: actions/setup-node@v4
# with:
# node-version: ${{ env.NODE_VERSION }}
# cache: 'npm'
#
# - name: Cache NPM Dependencies
# uses: actions/cache@v4
# with:
# path: |
# node_modules
# ~/.cache
# dist
# key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
#
# - name: Test Affected
# run: npx nx affected --target=test --parallel=3
test:
name: Test Affected
needs: setup
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
# We need to fetch all branches and commits so that Nx affected has a base to compare against.
fetch-depth: 0
# Derive appropriate SHAs for base and head for `nx affected` commands
- uses: nrwl/nx-set-shas@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- name: Cache NPM Dependencies
uses: actions/cache@v4
with:
path: |
node_modules
~/.cache
dist
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}

- name: Test Affected
run: npx nx affected --target=test --parallel=3

build:
name: Build Affected
Expand Down Expand Up @@ -132,6 +131,33 @@ jobs:
- name: Build Affected
run: npx nx affected -t build build-storybook --parallel=3

e2e:
name: E2E Affected
needs: [build]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: nrwl/nx-set-shas@v3
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- name: Cache NPM Dependencies
uses: actions/cache@v4
with:
path: |
node_modules
~/.cache
dist
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}

- name: E2E Affected
run: npx nx affected --target=e2e --parallel=3

# publish-storybook:
# name: Publish Storybook
# needs: [build, lint]
Expand Down Expand Up @@ -161,7 +187,7 @@ jobs:

deploy-front-end:
name: Deploy Front-End
needs: [build, lint]
needs: [test, build, lint, e2e]
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -203,7 +229,7 @@ jobs:

deploy-server:
name: Deploy Server
needs: [build, lint]
needs: [test, build, lint, e2e]
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -239,33 +265,3 @@ jobs:
- name: Deploy Prod Server
if: ${{ env.is-main-branch == 'true' }}
run: npx nx affected --target=deploy --exclude='*,!tag:backend' --configuration=production

# release:
# name: Release Affected
# needs: [build, lint]
# runs-on: ubuntu-latest
#
# steps:
# - uses: actions/checkout@v4
# with:
# # We need to fetch all branches and commits so that Nx affected has a base to compare against.
# fetch-depth: 0
# # Derive appropriate SHAs for base and head for `nx affected` commands
# - uses: nrwl/nx-set-shas@v3
# - uses: actions/setup-node@v4
# with:
# node-version: 20
# cache: 'npm'
#
# - name: Cache NPM Dependencies
# uses: actions/cache@v4
# with:
# path: |
# node_modules
# ~/.cache
# dist
# key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
#
# - name: Release Affected
## if: ${{ env.is-main-branch == 'true' }}
# run: npx nx affected -t nx-release-publish --parallel=3
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,6 @@ dist

# Nx Cache
.nx

# Temporaty files
tmp
13 changes: 13 additions & 0 deletions global-setup.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { execSync } from 'child_process';
import startLocalRegistry from './tools/scripts/start-local-registry';
import stopLocalRegistry from './tools/scripts/stop-local-registry';

export async function setup() {
await startLocalRegistry();
execSync('npm install -D @app-speed/esbuild-meta@e2e --force');
}

export async function teardown() {
stopLocalRegistry();
execSync('npm uninstall @app-speed/esbuild-meta');
}
6 changes: 5 additions & 1 deletion packages/esbuild-meta/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
"@nx/dependency-checks": [
"error",
{
"ignoredFiles": ["{projectRoot}/vite.config.{js,ts,mjs,mts}"]
"ignoredFiles": [
"{projectRoot}/e2e/**/*",
"{projectRoot}/vitest.config.e2e.{js,ts,mjs,mts}",
"{projectRoot}/vite.config.{js,ts,mjs,mts}"
]
}
]
}
Expand Down
16 changes: 16 additions & 0 deletions packages/esbuild-meta/e2e/__snapshots__/filter.test.e2e.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`filter command > should have a help option 1`] = `
"esbuild-meta filter
Filters the meta file to only include chunks required by specified entry points
Options:
-s, --statsPath The path to the stats.json file [string] [required]
-o, --outPath The path where the new file should be saved [string] [default: "initial-stats.json"]
--excludeDynamicImports, --eDI Should the dynamic imports be filtered out of the output chunk imports [boolean] [default: false]
-e, --entryPoints Entry points that should be considered for the bundle [array] [default: ["main-","polyfills-"]]
-v, --version Show version number [boolean]
-h, --help Show help [boolean]
"
`;
13 changes: 13 additions & 0 deletions packages/esbuild-meta/e2e/__snapshots__/root.test.e2e.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`--help > should show help 1`] = `
"esbuild-meta [command]
Commands:
esbuild-meta filter Filters the meta file to only include chunks required by specified entry points [aliases: f]
Options:
-v, --version Show version number [boolean]
-h, --help Show help [boolean]
"
`;
28 changes: 28 additions & 0 deletions packages/esbuild-meta/e2e/filter.test.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { describe, expect, it } from 'vitest';
import { cliProcess } from './utils.js';
import { DEMAND_STATS_PATH } from '../src/lib/filter-meta.js';
import { INVALID_FILE_PATH_ERROR_MSG } from '../src/lib/utils.js';

describe('filter command', () => {
it('should have a help option', async () => {
const { stdout, stderr, code } = await cliProcess('esbuild-meta filter --help');
expect(stdout).toMatchSnapshot();
expect(stderr).toBeFalsy();
expect(code).toBe(0);
});

it('should demand stats path option', async () => {
const { stdout, stderr, code } = await cliProcess('esbuild-meta filter');
expect(stderr).toContain(DEMAND_STATS_PATH);
expect(stdout).toBeFalsy();
expect(code).toBe(1);
});

it('should throw if stats path does not point to a file', async () => {
const INVALID_STATS_FILE = 'invalid-path.json';
const { stdout, stderr, code } = await cliProcess(`esbuild-meta filter --statsPath ${INVALID_STATS_FILE}`);
expect(stderr).toContain(INVALID_FILE_PATH_ERROR_MSG('invalid-path.json'));
expect(stdout).toBeFalsy();
expect(code).toBe(1);
});
});
32 changes: 32 additions & 0 deletions packages/esbuild-meta/e2e/root.test.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, it, expect, beforeAll } from 'vitest';
import { version } from '../package.json';

import { cliProcess, CliProcessOutput } from './utils.js';

describe('--help', () => {
let output: CliProcessOutput;

beforeAll(async () => {
output = await cliProcess('esbuild-meta --help');
});

it('should show help', async () => {
const { stdout, stderr, code } = output;
expect(stdout).toMatchSnapshot();
expect(stderr).toBeFalsy();
expect(code).toBe(0);
});

it('should alias to -h', async () => {
expect(await cliProcess('esbuild-meta --help')).toEqual(output);
});
});

describe('--version', () => {
it('should show version', async () => {
const {stdout, stderr, code} = await cliProcess('esbuild-meta --version');
expect(stdout).toContain(version);
expect(stderr).toBeFalsy();
expect(code).toBe(0);
});
});
19 changes: 19 additions & 0 deletions packages/esbuild-meta/e2e/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { spawn } from 'node:child_process';

export type CliProcessOutput = {
stdout: string;
stderr: string;
code: number | null;
}

export const cliProcess = (command: string) => {
return new Promise<CliProcessOutput>((resolve) => {
const process = spawn(command, [], { stdio: 'pipe', shell: true });

let stdout = '';
let stderr = '';
process.stdout.on('data', (data) => stdout += String(data));
process.stderr.on('data', (data) => stderr += String(data));
process.on('close', code => resolve({ stdout, stderr, code }));
})
}
17 changes: 13 additions & 4 deletions packages/esbuild-meta/project.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "esbuild-meta",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"root": "packages/esbuild-meta",
"sourceRoot": "packages/esbuild-meta/src",
"projectType": "library",
"tags": [],
"projectType": "app",
"targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
Expand All @@ -17,16 +17,25 @@
"format": ["esm"],
"minify": true,
"platform": "node",
"bundle": true,
"thirdParty": true
"bundle": true
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"]
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"reportsDirectory": "../../coverage/packages/esbuild-meta"
}
},
"e2e": {
"executor": "@nx/vite:test",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}-e2e"],
"options": { "config": "packages/esbuild-meta/vitest.config.e2e.ts" },
"dependsOn": ["build"]
}
}
}
14 changes: 8 additions & 6 deletions packages/esbuild-meta/src/lib/filter-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import {
makeJson,
} from './utils.js';

const distPath = {
alias: 'd',
export const DEMAND_STATS_PATH = 'The path to a stats.json file is required';

const statsPath = {
alias: 's',
type: 'string',
default: 'dist',
description: 'The path to the stats.json file'
description: 'The path to the stats.json file',
demandOption: DEMAND_STATS_PATH,
} as const satisfies Options;

const outPath = {
Expand All @@ -37,7 +39,7 @@ const entryPoints = {
description: 'Entry points that should be considered for the bundle',
} as const satisfies Options;

const filterMetaOptions = { distPath, outPath, excludeDynamicImports, entryPoints };
const filterMetaOptions = { statsPath, outPath, excludeDynamicImports, entryPoints };

type FilterMetaOptions = InferredOptionTypes<typeof filterMetaOptions>;
type FilterMetaCommandModule = CommandModule<unknown, FilterMetaOptions>;
Expand All @@ -47,7 +49,7 @@ const filterMetaBuilder: CommandBuilder<unknown, FilterMetaOptions> = (argv: Arg
}

const filterMetaHandler: FilterMetaCommandModule['handler'] = (argv: FilterMetaOptions) => {
const meta = getJson<Metafile>([argv.distPath]);
const meta = getJson<Metafile>(argv.statsPath);
const entryPoints = extractEntryPoints(meta, argv.entryPoints);
filterMetaFromEntryPoints(meta, entryPoints);
if (argv.excludeDynamicImports) {
Expand Down
14 changes: 11 additions & 3 deletions packages/esbuild-meta/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { join, normalize } from 'node:path';
import { Metafile } from 'esbuild';

export function getJson<T = any>(path: string[]) {
return JSON.parse(readFileSync(join(...path), {encoding: 'utf-8'})) as T;
export const INVALID_FILE_PATH_ERROR_MSG = (path: string) => `No file found at ${path}`;

export function getJson<T = any>(path: string) {
const normalizedPath = normalize(path);
try {
return JSON.parse(readFileSync(normalizedPath, {encoding: 'utf-8'})) as T;
}
catch (e) {
throw new Error(INVALID_FILE_PATH_ERROR_MSG(normalizedPath));
}
}

export function makeJson(path: string, file: any) {
Expand Down
Loading

0 comments on commit d115914

Please sign in to comment.