Skip to content
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

chore(testing): add lerna-smoke-tests #14347

Merged
merged 6 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ jobs:
NX_E2E_RUN_CYPRESS: 'false'
NX_VERBOSE_LOGGING: 'false'
steps:
- run:
name: Configure git metadata (needed for lerna smoke tests)
command: |
git config --global user.email [email protected]
git config --global user.name "Test Test"
- run:
name: Set dynamic nx run variable
command: |
Expand Down
11 changes: 11 additions & 0 deletions e2e/lerna-smoke-tests/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
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-lerna-smoke-tests',
preset: '../../jest.preset.js',
};
11 changes: 11 additions & 0 deletions e2e/lerna-smoke-tests/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "e2e-lerna-smoke-tests",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "e2e/lerna-smoke-tests",
"projectType": "application",
"targets": {
"e2e": {},
"run-e2e-tests": {}
},
"implicitDependencies": ["nx", "devkit"]
}
79 changes: 79 additions & 0 deletions e2e/lerna-smoke-tests/src/lerna-smoke-tests.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* These minimal smoke tests are here to ensure that we do not break assumptions on the lerna side
* when making updates to nx or @nrwl/devkit.
*/

import {
cleanupLernaWorkspace,
newLernaWorkspace,
runLernaCLI,
tmpProjPath,
updateJson,
} from '@nrwl/e2e/utils';

expect.addSnapshotSerializer({
serialize(str: string) {
return (
str
// Not all package managers print the package.json path in the output
.replace(tmpProjPath(), '')
.replace('/private', '')
.replace('/packages/package-1', '')
// We trim each line to reduce the chances of snapshot flakiness
.split('\n')
.map((r) => r.trim())
.join('\n')
);
},
test(val: string) {
return val != null && typeof val === 'string';
},
});

describe('Lerna Smoke Tests', () => {
beforeAll(() => newLernaWorkspace());
afterAll(() => cleanupLernaWorkspace());

// `lerna repair` builds on top of `nx repair` and runs all of its generators
describe('lerna repair', () => {
// If this snapshot fails it means that nx repair generators are making assumptions which don't hold true for lerna workspaces
it('should complete successfully on a new lerna workspace', async () => {
expect(runLernaCLI(`repair`)).toMatchInlineSnapshot(`

> Lerna No changes were necessary. This workspace is up to date!


`);
}, 1000000);
});

// `lerna run` delegates to the nx task runner behind the scenes
describe('lerna run', () => {
it('should complete successfully on a new lerna workspace', async () => {
runLernaCLI('create package-1 -y');
updateJson('packages/package-1/package.json', (json) => ({
...json,
scripts: {
...(json.scripts || {}),
'print-name': 'echo test-package-1',
},
}));

expect(runLernaCLI(`run print-name`)).toMatchInlineSnapshot(`

> package-1:print-name

> [email protected] print-name
> echo test-package-1
test-package-1



> Lerna (powered by Nx) Successfully ran target print-name for project package-1



`);
}, 1000000);
});
});
13 changes: 13 additions & 0 deletions e2e/lerna-smoke-tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"types": ["node", "jest"]
},
"include": [],
"files": [],
"references": [
{
"path": "./tsconfig.spec.json"
}
]
}
20 changes: 20 additions & 0 deletions e2e/lerna-smoke-tests/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
157 changes: 156 additions & 1 deletion e2e/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
workspaceRoot,
} from '@nrwl/devkit';
import { angularCliVersion } from '@nrwl/workspace/src/utils/versions';
import { dump } from '@zkochan/js-yaml';
import { ChildProcess, exec, execSync, ExecSyncOptions } from 'child_process';
import {
copySync,
Expand Down Expand Up @@ -46,7 +47,8 @@ export function getPublishedVersion(): string {
export function detectPackageManager(dir: string = ''): PackageManager {
return existsSync(join(dir, 'yarn.lock'))
? 'yarn'
: existsSync(join(dir, 'pnpm-lock.yaml'))
: existsSync(join(dir, 'pnpm-lock.yaml')) ||
existsSync(join(dir, 'pnpm-workspace.yaml'))
? 'pnpm'
: 'npm';
}
Expand Down Expand Up @@ -374,6 +376,106 @@ export function newProject({
}
}

export function newLernaWorkspace({
name = uniq('lerna-proj'),
packageManager = getSelectedPackageManager(),
} = {}): string {
try {
const projScope = name;
projName = name;

const pm = getPackageManagerCommand({ packageManager });

createNonNxProjectDirectory(projScope, packageManager !== 'pnpm');

if (packageManager === 'pnpm') {
updateFile(
'pnpm-workspace.yaml',
dump({
packages: ['packages/*'],
})
);
updateFile(
'.npmrc',
'prefer-frozen-lockfile=false\nstrict-peer-dependencies=false\nauto-install-peers=true'
);
}

if (process.env.NX_VERBOSE_LOGGING == 'true') {
logInfo(`NX`, `E2E test has created a lerna workspace: ${tmpProjPath()}`);
}

// We need to force the real latest version of lerna to depend on our locally published version of nx
updateJson(`package.json`, (json) => {
// yarn workspaces can only be enabled in private projects
json.private = true;

const nxVersion = getPublishedVersion();
const overrides = {
...json.overrides,
nx: nxVersion,
'@nrwl/devkit': nxVersion,
};
if (packageManager === 'pnpm') {
json.pnpm = {
...json.pnpm,
overrides: {
...json.pnpm?.overrides,
...overrides,
},
};
} else if (packageManager === 'yarn') {
json.resolutions = {
...json.resolutions,
...overrides,
};
} else {
json.overrides = overrides;
}
return json;
});

/**
* Again, in order to ensure we override the required version relationships, we first install lerna as a devDep
* before running `lerna init`.
*/
execSync(
`${pm.addDev} lerna@${getLatestLernaVersion()}${
packageManager === 'pnpm'
? ' --workspace-root'
: packageManager === 'yarn'
? ' -W'
: ''
}`,
{
cwd: tmpProjPath(),
stdio: isVerbose() ? 'inherit' : 'pipe',
env: { CI: 'true', ...process.env },
encoding: 'utf-8',
}
);

execSync(`${pm.runLerna} init`, {
cwd: tmpProjPath(),
stdio: isVerbose() ? 'inherit' : 'pipe',
env: { CI: 'true', ...process.env },
encoding: 'utf-8',
});

execSync(pm.install, {
cwd: tmpProjPath(),
stdio: isVerbose() ? 'inherit' : 'pipe',
env: { CI: 'true', ...process.env },
encoding: 'utf-8',
});

return projScope;
} catch (e) {
logError(`Failed to set up lerna workspace for e2e tests.`, e.message);
throw e;
}
}

const KILL_PORT_DELAY = 5000;

export async function killPort(port: number): Promise<boolean> {
Expand Down Expand Up @@ -416,6 +518,14 @@ export async function cleanupProject(opts?: RunCmdOpts) {
}
}

export function cleanupLernaWorkspace() {
if (isCI) {
try {
removeSync(tmpProjPath());
} catch (e) {}
}
}

export function runCypressTests() {
if (process.env.NX_E2E_RUN_CYPRESS === 'true') {
ensureCypressInstallation();
Expand Down Expand Up @@ -629,6 +739,42 @@ export function runCLI(
}
}

export function runLernaCLI(
command: string,
opts: RunCmdOpts = {
silenceError: false,
env: undefined,
}
): string {
try {
const pm = getPackageManagerCommand();
const logs = execSync(`${pm.runLerna} ${command}`, {
cwd: opts.cwd || tmpProjPath(),
env: { CI: 'true', ...(opts.env || getStrippedEnvironmentVariables()) },
encoding: 'utf-8',
stdio: 'pipe',
maxBuffer: 50 * 1024 * 1024,
});
const r = stripConsoleColors(logs);

if (isVerbose()) {
console.log(logs);
}

return r;
} catch (e) {
if (opts.silenceError) {
return stripConsoleColors(e.stdout?.toString() + e.stderr?.toString());
} else {
logError(
`Original command: ${command}`,
`${e.stdout?.toString()}\n\n${e.stderr?.toString()}`
);
throw e;
}
}
}

/**
* Remove log colors for fail proof string search
* @param log
Expand Down Expand Up @@ -882,6 +1028,7 @@ export function getPackageManagerCommand({
addProd: string;
addDev: string;
list: string;
runLerna: string;
} {
const npmMajorVersion = getNpmMajorVersion();
const publishedVersion = getPublishedVersion();
Expand All @@ -899,6 +1046,7 @@ export function getPackageManagerCommand({
addProd: `npm install --legacy-peer-deps`,
addDev: `npm install --legacy-peer-deps -D`,
list: 'npm ls --depth 10',
runLerna: `npx lerna`,
},
yarn: {
// `yarn create nx-workspace` is failing due to wrong global path
Expand All @@ -911,6 +1059,7 @@ export function getPackageManagerCommand({
addProd: `yarn add`,
addDev: `yarn add -D`,
list: 'npm ls --depth 10',
runLerna: `yarn lerna`,
},
// Pnpm 3.5+ adds nx to
pnpm: {
Expand All @@ -923,6 +1072,7 @@ export function getPackageManagerCommand({
addProd: `pnpm add`,
addDev: `pnpm add -D`,
list: 'npm ls --depth 10',
runLerna: `pnpm exec lerna`,
},
}[packageManager.trim() as PackageManager];
}
Expand All @@ -932,6 +1082,11 @@ function getNpmMajorVersion(): string {
return npmMajorVersion;
}

function getLatestLernaVersion(): string {
const lernaVersion = execSync(`npm view lerna version`).toString().trim();
return lernaVersion;
}

export const packageManagerLockFile = {
npm: 'package-lock.json',
yarn: 'yarn.lock',
Expand Down
3 changes: 3 additions & 0 deletions nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@
{
"env": "SELECTED_CLI"
},
{
"env": "SELECTED_PM"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discovered this was missing from the cache key when switching package managers locally to test this

},
{
"env": "NX_E2E_CI_CACHE_KEY"
}
Expand Down