Skip to content
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
19 changes: 19 additions & 0 deletions __mocks__/ora.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// CJS mock for ora (ESM-only in v9+) to allow schematics tests to run under Jest
'use strict';

const mockSpinner = {
start: () => mockSpinner,
stop: () => mockSpinner,
succeed: () => mockSpinner,
fail: () => mockSpinner,
warn: () => mockSpinner,
info: () => mockSpinner,
text: '',
prefixText: '',
};

const ora = () => mockSpinner;
ora.default = ora;

module.exports = ora;
module.exports.default = ora;
13 changes: 5 additions & 8 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import type { Config } from 'jest';
import {
createDefaultEsmPreset,
createDefaultPreset,
ESM_TS_TRANSFORM_PATTERN,
TS_EXT_TO_TREAT_AS_ESM,
} from 'ts-jest';

const esModules = ['@angular'];
import { createDefaultPreset } from 'ts-jest';

const config: Config = {
...createDefaultPreset(),
testPathIgnorePatterns: ['/node_modules/', '/lib/', 'cypress'],
snapshotFormat: { escapeString: true, printBasicPrototype: true },
moduleNameMapper: {
// ora v9+ is ESM-only; mock it so @angular-devkit/schematics/testing can load under Jest (CJS)
'^ora$': '<rootDir>/__mocks__/ora.js',
},
};

export default config;
9 changes: 9 additions & 0 deletions libs/single-spa-community-angular/webpack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,16 @@ function mergeConfigs(
}
}

declare const jest: any;
function createLogger() {
// Under Jest, skip the NX logger and fall back to console.warn.
// Tests that care about suppressing output should wrap their call with `skipConsoleLogging`.
if (typeof jest !== 'undefined') {
return {
warn: (message: string) => console.warn(message),
};
}

try {
// If we're in an Nx workspace then use its logger.
// eslint-disable-next-line
Expand Down
8 changes: 6 additions & 2 deletions schematics/ng-add/rules/update-configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,16 @@ function updateTSConfig(tree: Tree, buildTarget: workspaces.TargetDefinition): v

const tsConfig = parse(buffer.toString());

// Only update `files` when it already exists in the tsconfig.
// Older Angular versions (≤15) used a `files` array to declare entry points
// (`main.ts` and `polyfills.ts`). We replace it with just `main.single-spa.ts`
// because polyfills are handled separately via the Webpack `entry` property.
// Newer Angular versions use `include`/`exclude` instead; those projects do not
// need this transformation.
if (!Array.isArray(tsConfig.files)) {
return;
}

// The "files" property will only contain path to `main.single-spa.ts` file,
// because we remove `polyfills` from Webpack `entry` property.
tsConfig.files = [normalize('src/main.single-spa.ts')];
tree.overwrite(tsConfigPath, JSON.stringify(tsConfig, null, 2));
}
Expand Down
6 changes: 4 additions & 2 deletions schematics/ng-add/tests/add-scripts.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UnitTestTree } from '@angular-devkit/schematics/testing';

import { createTestRunner, VERSION } from './utils';
import { createTestRunner, skipConsoleLogging, VERSION } from './utils';

const workspaceOptions = {
name: 'workspace',
Expand Down Expand Up @@ -40,7 +40,9 @@ describe('ng-add', () => {
await generateApplication('first-cool-app');
await generateApplication('second-cool-app');

await testRunner.runSchematic('ng-add', { project: 'first-cool-app' }, workspaceTree);
await skipConsoleLogging(() => {
return testRunner.runSchematic('ng-add', { project: 'first-cool-app' }, workspaceTree);
});

const tree = await testRunner.runSchematic(
'ng-add',
Expand Down
128 changes: 77 additions & 51 deletions schematics/ng-add/tests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { normalize } from 'path';
import { UnitTestTree } from '@angular-devkit/schematics/testing';

import { Schema as NgAddOptions } from '../schema';
import { createTestRunner, createWorkspace, getFileContent, VERSION } from './utils';
import {
createTestRunner,
createWorkspace,
getFileContent,
skipConsoleLogging,
VERSION,
} from './utils';

const workspaceOptions = {
name: 'ss-workspace',
Expand Down Expand Up @@ -30,67 +36,79 @@ describe('ng-add', () => {
});

test('should run ng-add', async () => {
const tree = await testRunner.runSchematic(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
});

expect(tree.files).toBeDefined();
});

test('should add single-spa and single-spa-angular to dependencies', async () => {
const tree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
});

const packageJSON = JSON.parse(getFileContent(tree, '/package.json'));
expect(packageJSON.dependencies['single-spa']).toBeDefined();
expect(packageJSON.dependencies['single-spa-angular']).toBeDefined();
});

test('should add style-laoder to devDependencies', async () => {
const tree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
});

const packageJSON = JSON.parse(getFileContent(tree, '/package.json'));
expect(packageJSON.devDependencies['style-loader']).toBeDefined();
});

test('should add @angular-builders/custom-webpack to devDependencies', async () => {
const tree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
});

const packageJSON = JSON.parse(getFileContent(tree, '/package.json'));
expect(packageJSON.devDependencies['@angular-builders/custom-webpack']).toBeDefined();
});

test('should add main-single-spa.ts', async () => {
const tree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
});

expect(
tree.files.indexOf('/projects/ss-angular-cli-app/src/main.single-spa.ts'),
).toBeGreaterThan(-1);
});

test('should use correct prefix for root', async () => {
const tree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app' },
appTree,
);
});

const mainModuleContent = getFileContent(
tree,
Expand All @@ -100,11 +118,13 @@ describe('ng-add', () => {
});

test('should not add router dependencies', async () => {
const tree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app', routing: false },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app', routing: false },
appTree,
);
});

const mainModuleContent = getFileContent(
tree,
Expand All @@ -114,11 +134,13 @@ describe('ng-add', () => {
});

test('should add router dependencies', async () => {
const tree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app', routing: true },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app', routing: true },
appTree,
);
});

const mainModuleContent = getFileContent(
tree,
Expand All @@ -128,11 +150,13 @@ describe('ng-add', () => {
});

test('should modify angular.json', async () => {
const tree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ routing: true, project: 'ss-angular-cli-app' },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ routing: true, project: 'ss-angular-cli-app' },
appTree,
);
});

const angularJSON = JSON.parse(getFileContent(tree, '/angular.json'));
const ssApp = angularJSON.projects['ss-angular-cli-app'];
Expand All @@ -155,11 +179,13 @@ describe('ng-add', () => {
});

test('should add build:single-spa:PROJECT_NAME npm script', async () => {
const tree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app', routing: true },
appTree,
);
const tree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app', routing: true },
appTree,
);
});

const packageJSON = JSON.parse(getFileContent(tree, '/package.json'));
expect(packageJSON.scripts['build:single-spa:ss-angular-cli-app']).toBeDefined();
Expand Down
10 changes: 7 additions & 3 deletions schematics/ng-add/tests/issue-168.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UnitTestTree } from '@angular-devkit/schematics/testing';

import { createTestRunner, VERSION } from './utils';
import { createTestRunner, skipConsoleLogging, VERSION } from './utils';

const workspaceOptions = {
name: 'workspace',
Expand Down Expand Up @@ -45,7 +45,9 @@ describe('https://github.com/single-spa/single-spa-angular/issues/168', () => {
// Act
appTree.overwrite('/angular.json', JSON.stringify(buildTarget));

await testRunner.runSchematic('ng-add', { project: 'first-cool-app' }, appTree);
await skipConsoleLogging(() => {
return testRunner.runSchematic('ng-add', { project: 'first-cool-app' }, appTree);
});

buildTarget = JSON.parse(`${appTree.get('/angular.json')!.content}`);
configurations = buildTarget.projects['first-cool-app'].architect.build.configurations;
Expand Down Expand Up @@ -73,7 +75,9 @@ describe('https://github.com/single-spa/single-spa-angular/issues/168', () => {
// Act
appTree.overwrite('/angular.json', JSON.stringify(buildTarget));

await testRunner.runSchematic('ng-add', { project: 'second-cool-app' }, appTree);
await skipConsoleLogging(() => {
return testRunner.runSchematic('ng-add', { project: 'second-cool-app' }, appTree);
});

buildTarget = JSON.parse(`${appTree.get('/angular.json')!.content}`);
configurations = buildTarget.projects['second-cool-app'].architect.build.configurations;
Expand Down
41 changes: 29 additions & 12 deletions schematics/ng-add/tests/issue-249.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { UnitTestTree } from '@angular-devkit/schematics/testing';
import * as JSON5 from 'json5';

import { Schema as NgAddOptions } from '../schema';
import { createWorkspace, createTestRunner, getFileContent, VERSION } from './utils';
import {
createWorkspace,
createTestRunner,
getFileContent,
VERSION,
skipConsoleLogging,
} from './utils';

const workspaceOptions = {
name: 'ss-workspace',
Expand All @@ -23,12 +29,21 @@ const appOptions = {

const angular10Comment = '/* Unexpected comment from Angular */';

// Simulate what angular 10 is doing adding a comment to the tsconfig file
// Simulate what Angular 10 does: prepend a comment AND include a `files` array.
// Newer Angular versions dropped `files` in favour of `include`/`exclude`, but
// Angular 10–15 shipped both. The comment would break JSON.parse; JSON5 handles it.
// https://github.com/single-spa/single-spa-angular/issues/249
function appendCommentToTsConfig(tree: UnitTestTree) {
let content = getFileContent(tree, '/projects/ss-angular-cli-app/tsconfig.app.json');
content = angular10Comment + '\n' + content;
tree.overwrite('/projects/ss-angular-cli-app/tsconfig.app.json', content);
function patchTsConfigToSimulateAngular10(tree: UnitTestTree) {
const tsConfig = JSON5.parse(
getFileContent(tree, '/projects/ss-angular-cli-app/tsconfig.app.json'),
);

// Inject the `files` entry that older Angular versions included.
tsConfig.files = ['src/main.ts', 'src/polyfills.ts'];

// Prepend the comment that Angular 10 adds to every tsconfig.
const patched = angular10Comment + '\n' + JSON.stringify(tsConfig, null, 2);
tree.overwrite('/projects/ss-angular-cli-app/tsconfig.app.json', patched);
}

describe('https://github.com/single-spa/single-spa-angular/issues/249', () => {
Expand All @@ -37,15 +52,17 @@ describe('https://github.com/single-spa/single-spa-angular/issues/249', () => {

beforeEach(async () => {
appTree = await createWorkspace(testRunner, appTree, workspaceOptions, appOptions);
appendCommentToTsConfig(appTree);
patchTsConfigToSimulateAngular10(appTree);
});

test('should update `tsconfig.app.json` and add `main.single-spa.ts` to `files`', async () => {
appTree = await testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app', routing: true },
appTree,
);
appTree = await skipConsoleLogging(() => {
return testRunner.runSchematic<NgAddOptions>(
'ng-add',
{ project: 'ss-angular-cli-app', routing: true },
appTree,
);
});

const expectedTsConfigPath = normalize('projects/ss-angular-cli-app/tsconfig.app.json');
const buffer: Buffer | null = appTree.read(expectedTsConfigPath);
Expand Down
Loading
Loading