Skip to content

Commit

Permalink
chore: initial e2e spec for lerna init (#3158)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHenry committed Jun 8, 2022
1 parent 479bf4c commit 6cf9be8
Show file tree
Hide file tree
Showing 14 changed files with 1,103 additions and 154 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Expand Up @@ -9,6 +9,10 @@ on:
pull_request:
types: [assigned, opened, synchronize, reopened, labeled]

concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
cancel-in-progress: true

jobs:
lint:
runs-on: ubuntu-latest
Expand All @@ -28,6 +32,25 @@ jobs:
- run: npm ci
- run: npm run ci:lint

e2e:
needs: [lint]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
# only e2e on latest LTS node
node-version: 16
- uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm -g i npm@8.10.0
- run: npm ci
- run: npm run e2e

test:
needs: [lint]
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Expand Up @@ -103,7 +103,7 @@ If you want to test out Lerna on local repos, you can leverage verdaccio as a lo
Open a new terminal window and run the following from the root of the workspace:

```sh
$ npm run e2e:run-verdaccio
$ npx lerna run --scope @lerna/e2e start-verdaccio
```

This will run verdaccio on http://localhost:4872
Expand Down
12 changes: 12 additions & 0 deletions e2e/.eslintrc.json
@@ -0,0 +1,12 @@
{
"extends": "../.eslintrc.yaml",
"parser": "@typescript-eslint/parser",
"rules": {
"import/extensions": "off",
"import/no-unresolved": "off",
"import/no-extraneous-dependencies": "off",
"node/no-missing-import": "off",
"node/no-unpublished-import": "off",
"node/no-unsupported-features/es-syntax": "off"
}
}
10 changes: 10 additions & 0 deletions e2e/jest.config.ts
@@ -0,0 +1,10 @@
export default {
transform: {
"^.+\\.[tj]sx?$": "ts-jest",
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "html"],
maxWorkers: 1,
globals: { "ts-jest": { tsconfig: "<rootDir>/tsconfig.spec.json" } },
displayName: "e2e",
testTimeout: 30000,
};
53 changes: 53 additions & 0 deletions e2e/lerna-init/lerna-init.spec.ts
@@ -0,0 +1,53 @@
import {
createEmptyDirectoryForWorkspace,
getPublishedVersion,
readFile,
removeWorkspace,
runCommandAsync,
} from "../utils";

describe("lerna init", () => {
afterEach(() => removeWorkspace());

it("should initialize a lerna workspace", async () => {
createEmptyDirectoryForWorkspace("lerna-init-test");

/**
* There is nothing about lerna init that is package manager specific, as no installation occurs
* as part of the command, so we simply use npx here and resolve from verdaccio.
*/
const output = await runCommandAsync(
`npx --registry=http://localhost:4872/ --yes lerna@${getPublishedVersion()} init`
);

expect(output.stderr).toMatchInlineSnapshot(`
"lerna notice cli v999.9.9-e2e.0
lerna info Initializing Git repository
lerna info Creating package.json
lerna info Creating lerna.json
lerna info Creating packages directory
lerna success Initialized Lerna files
"
`);

expect(readFile("lerna.json")).toMatchInlineSnapshot(`
"{
\\"packages\\": [
\\"packages/*\\"
],
\\"version\\": \\"0.0.0\\"
}
"
`);
expect(readFile("package.json")).toMatchInlineSnapshot(`
"{
\\"name\\": \\"root\\",
\\"private\\": true,
\\"devDependencies\\": {
\\"lerna\\": \\"^999.9.9-e2e.0\\"
}
}
"
`);
});
});
15 changes: 15 additions & 0 deletions e2e/package.json
@@ -0,0 +1,15 @@
{
"name": "@lerna/e2e",
"private": true,
"description": "e2e tests for lerna packages",
"scripts": {
"pree2e": "npm run start-verdaccio &",
"e2e": "npm run prepare-local-publish && npm run publish-to-verdaccio -- 999.9.9-e2e.0 && PUBLISHED_VERSION=999.9.9-e2e.0 npm run run-tests",
"poste2e": "npm run kill-verdaccio",
"start-verdaccio": "verdaccio --config ./local-registry/config.yml --listen 4872",
"kill-verdaccio": "kill $(lsof -t -i:4872)",
"prepare-local-publish": "./prepare-local-publish.sh",
"publish-to-verdaccio": "npm_config_registry=\"http://localhost:4872/\" ./publish-to-verdaccio.sh",
"run-tests": "jest --config ./jest.config.ts"
}
}
13 changes: 7 additions & 6 deletions e2e/prepare-local-publish.sh
@@ -1,11 +1,12 @@
#!/usr/bin/env bash

# pwd should be the root of the workspace
if [ ! -f ./lerna.json ]; then
echo "Error: This script must be run from the root of the workspace"
SCRIPT_DIR=$(dirname "$0")

# pwd when running the command should be the e2e directory
if [ $SCRIPT_DIR != "." ]; then
echo "Error: This script must be run from the e2e directory"
exit 1
fi
workspaceRoot=$(pwd)

cp ./e2e/lerna.json.localpublish ./dist/lerna.json
cp ./e2e/package.json.localpublish ./dist/package.json
cp ./lerna.json.localpublish ../dist/lerna.json
cp ./package.json.localpublish ../dist/package.json
12 changes: 10 additions & 2 deletions e2e/publish-to-verdaccio.sh
@@ -1,5 +1,13 @@
#!/usr/bin/env bash

SCRIPT_DIR=$(dirname "$0")

# pwd when running the command should be the e2e directory
if [ $SCRIPT_DIR != "." ]; then
echo "Error: This script must be run from the e2e directory"
exit 1
fi

VERSION=$1
NPM_REGISTRY=`npm config get registry`

Expand All @@ -14,9 +22,9 @@ if [[ ! $NPM_REGISTRY == http://localhost* ]]; then
fi

# Change to the prepared dist directory
cd ./dist
cd ../dist

echo "Updating the package versions in ./dist to use $VERSION"
echo "Updating the package versions in $(pwd)/dist to use $VERSION"

echo ""

Expand Down
18 changes: 18 additions & 0 deletions e2e/tsconfig.json
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"module": "esnext",
"moduleResolution": "node",
"target": "es2017",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"types": ["node", "jest"]
},
"include": [],
"files": [],
"references": [
{
"path": "./tsconfig.spec.json"
}
]
}
20 changes: 20 additions & 0 deletions e2e/tsconfig.spec.json
@@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"**/*.test.ts",
"**/*.spec.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.d.ts",
"jest.config.ts"
]
}
94 changes: 94 additions & 0 deletions e2e/utils/index.ts
@@ -0,0 +1,94 @@
import { exec } from "child_process";
import { ensureDirSync, readFileSync, removeSync } from "fs-extra";
import isCI from "is-ci";
import { dirSync } from "tmp";

export function getPublishedVersion(): string {
process.env.PUBLISHED_VERSION =
process.env.PUBLISHED_VERSION ||
// fallback to latest if built package is missing
"latest";
return process.env.PUBLISHED_VERSION;
}

interface RunCmdOpts {
silenceError?: boolean;
env?: Record<string, string>;
cwd?: string;
silent?: boolean;
}

export const e2eRoot = isCI ? dirSync({ prefix: "lerna-e2e-" }).name : "/tmp/lerna-e2e";

export const e2eCwd = e2eRoot;
ensureDirSync(e2eCwd);

let projName: string;

export function uniq(prefix: string) {
return `${prefix}${Math.floor(Math.random() * 10000000)}`;
}

export function createEmptyDirectoryForWorkspace(name: string): void {
projName = name;
const workspacePath = tmpProjPath();
ensureDirSync(workspacePath);
}

export function removeWorkspace() {
try {
removeSync(tmpProjPath());
// eslint-disable-next-line no-empty
} catch {}
}

export function readFile(f: string) {
const ff = f.startsWith("/") ? f : tmpProjPath(f);
return readFileSync(ff, "utf-8");
}

export function runCommandAsync(
command: string,
opts: RunCmdOpts = {
silenceError: false,
env: undefined,
}
): Promise<{ stdout: string; stderr: string; combinedOutput: string }> {
return new Promise((resolve, reject) => {
exec(
command,
{
cwd: tmpProjPath(),
env: {
...(opts.env || process.env),
FORCE_COLOR: "false",
},
encoding: "utf-8",
},
(err, stdout, stderr) => {
if (!opts.silenceError && err) {
reject(err);
}
resolve({
stdout: stripConsoleColors(stdout),
stderr: stripConsoleColors(stderr),
combinedOutput: stripConsoleColors(`${stdout}${stderr}`),
});
}
);
});
}

/**
* Remove log colors for fail proof string search
* @param log
* @returns
*/
function stripConsoleColors(log: string): string {
// eslint-disable-next-line no-control-regex
return log.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
}

export function tmpProjPath(path?: string) {
return path ? `${e2eCwd}/${projName}/${path}` : `${e2eCwd}/${projName}`;
}

0 comments on commit 6cf9be8

Please sign in to comment.