443 lines
11 KiB
TypeScript
443 lines
11 KiB
TypeScript
import {
|
|
InspectType,
|
|
NodeExecuteBuilder,
|
|
NodeExecuteBuilderOptions
|
|
} from './execute.builder';
|
|
import { TestLogger } from '@angular-devkit/architect/testing';
|
|
import { normalize } from '@angular-devkit/core';
|
|
import { of } from 'rxjs';
|
|
import { cold } from 'jasmine-marbles';
|
|
|
|
jest.mock('child_process');
|
|
let { fork } = require('child_process');
|
|
jest.mock('tree-kill');
|
|
let treeKill = require('tree-kill');
|
|
|
|
class MockArchitect {
|
|
getBuilderConfiguration() {
|
|
return {
|
|
config: 'testConfig'
|
|
};
|
|
}
|
|
|
|
run() {
|
|
return cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
});
|
|
}
|
|
|
|
getBuilderDescription() {
|
|
return of({
|
|
description: 'testDescription'
|
|
});
|
|
}
|
|
|
|
validateBuilderOptions() {
|
|
return of({
|
|
options: {}
|
|
});
|
|
}
|
|
}
|
|
|
|
describe('NodeExecuteBuilder', () => {
|
|
let builder: NodeExecuteBuilder;
|
|
let architect: MockArchitect;
|
|
let logger: TestLogger;
|
|
let testOptions: NodeExecuteBuilderOptions;
|
|
|
|
beforeEach(() => {
|
|
fork.mockReturnValue({
|
|
pid: 123
|
|
});
|
|
treeKill.mockImplementation((pid, signal, callback) => {
|
|
callback();
|
|
});
|
|
logger = new TestLogger('test');
|
|
architect = new MockArchitect();
|
|
builder = new NodeExecuteBuilder({
|
|
workspace: <any>{
|
|
root: '/root'
|
|
},
|
|
logger,
|
|
host: <any>{},
|
|
architect: <any>architect
|
|
});
|
|
testOptions = {
|
|
inspect: true,
|
|
args: [],
|
|
buildTarget: 'nodeapp:build',
|
|
port: 9229,
|
|
waitUntilTargets: [],
|
|
host: 'localhost'
|
|
};
|
|
});
|
|
|
|
it('should build the application and start the built file', () => {
|
|
const getBuilderConfiguration = spyOn(
|
|
architect,
|
|
'getBuilderConfiguration'
|
|
).and.callThrough();
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: testOptions
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
expect(getBuilderConfiguration).toHaveBeenCalledWith({
|
|
project: 'nodeapp',
|
|
target: 'build',
|
|
overrides: {
|
|
watch: true
|
|
}
|
|
});
|
|
expect(fork).toHaveBeenCalledWith('outfile.js', [], {
|
|
execArgv: ['--inspect=localhost:9229']
|
|
});
|
|
expect(treeKill).toHaveBeenCalledTimes(1);
|
|
expect(fork).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
describe('--inspect', () => {
|
|
describe('inspect', () => {
|
|
it('should inspect the process', () => {
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: {
|
|
...testOptions,
|
|
inspect: InspectType.Inspect
|
|
}
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
expect(fork).toHaveBeenCalledWith('outfile.js', [], {
|
|
execArgv: ['--inspect=localhost:9229']
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('inspect-brk', () => {
|
|
it('should inspect and break at beginning of execution', () => {
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: {
|
|
...testOptions,
|
|
inspect: InspectType.InspectBrk
|
|
}
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
expect(fork).toHaveBeenCalledWith('outfile.js', [], {
|
|
execArgv: ['--inspect-brk=localhost:9229']
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('--host', () => {
|
|
describe('0.0.0.0', () => {
|
|
it('should inspect the process on host 0.0.0.0', () => {
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: {
|
|
...testOptions,
|
|
host: '0.0.0.0'
|
|
}
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
expect(fork).toHaveBeenCalledWith('outfile.js', [], {
|
|
execArgv: ['--inspect=0.0.0.0:9229']
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('--port', () => {
|
|
describe('1234', () => {
|
|
it('should inspect the process on port 1234', () => {
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: {
|
|
...testOptions,
|
|
port: 1234
|
|
}
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
expect(fork).toHaveBeenCalledWith('outfile.js', [], {
|
|
execArgv: ['--inspect=localhost:1234']
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should log errors from killing the process', () => {
|
|
treeKill.mockImplementation((pid, signal, callback) => {
|
|
callback(new Error('Error Message'));
|
|
});
|
|
const loggerError = spyOn(logger, 'error');
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: testOptions
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
expect(loggerError.calls.argsFor(1)).toEqual(['Error Message']);
|
|
});
|
|
|
|
it('should log errors from killing the process on windows', () => {
|
|
treeKill.mockImplementation((pid, signal, callback) => {
|
|
callback([new Error('error'), '', 'Error Message']);
|
|
});
|
|
const loggerError = spyOn(logger, 'error');
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: testOptions
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
expect(loggerError.calls.argsFor(1)).toEqual(['Error Message']);
|
|
});
|
|
|
|
it('should build the application and start the built file with options', () => {
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: {
|
|
...testOptions,
|
|
inspect: false,
|
|
args: ['arg1', 'arg2']
|
|
}
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
expect(fork).toHaveBeenCalledWith('outfile.js', ['arg1', 'arg2'], {
|
|
execArgv: []
|
|
});
|
|
});
|
|
|
|
it('should warn users who try to use it in production', () => {
|
|
spyOn(architect, 'validateBuilderOptions').and.returnValue(
|
|
of({
|
|
options: {
|
|
optimization: true
|
|
}
|
|
})
|
|
);
|
|
spyOn(logger, 'warn');
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: {
|
|
...testOptions,
|
|
inspect: false,
|
|
args: ['arg1', 'arg2']
|
|
}
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
expect(logger.warn).toHaveBeenCalled();
|
|
});
|
|
|
|
describe('waitUntilTasks', () => {
|
|
beforeEach(() => {
|
|
spyOn(architect, 'getBuilderConfiguration').and.callFake(opts => {
|
|
if (opts.project === 'project1' && opts.target === 'target1')
|
|
return { config: 'builderConfig1' };
|
|
if (opts.project === 'project2' && opts.target === 'target2')
|
|
return { config: 'builderConfig2' };
|
|
return new MockArchitect().getBuilderConfiguration();
|
|
});
|
|
|
|
spyOn(architect, 'validateBuilderOptions').and.callFake(opts => {
|
|
if (
|
|
opts.config === 'builderConfig1' ||
|
|
opts.config === 'builderConfig2'
|
|
)
|
|
return of(opts);
|
|
return new MockArchitect().validateBuilderOptions();
|
|
});
|
|
});
|
|
|
|
it('should run the tasks before starting the build', () => {
|
|
spyOn(architect, 'run').and.callFake(opts => {
|
|
if (opts.config == 'builderConfig1') return of({ success: true });
|
|
if (opts.config == 'builderConfig2') return of({ success: true });
|
|
return new MockArchitect().run();
|
|
});
|
|
|
|
expect(
|
|
builder.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: {
|
|
...testOptions,
|
|
waitUntilTargets: ['project1:target1', 'project2:target2']
|
|
}
|
|
})
|
|
).toBeObservable(
|
|
cold('--a--b--a', {
|
|
a: {
|
|
success: true,
|
|
outfile: 'outfile.js'
|
|
},
|
|
b: {
|
|
success: false,
|
|
outfile: 'outfile.js'
|
|
}
|
|
})
|
|
);
|
|
});
|
|
|
|
it('should not run the build if any of the tasks fail', done => {
|
|
spyOn(architect, 'run').and.callFake(opts => {
|
|
if (opts.config == 'builderConfig1') return of({ success: true });
|
|
if (opts.config == 'builderConfig2')
|
|
return of({ other: true }, { success: false });
|
|
return new MockArchitect().run();
|
|
});
|
|
|
|
const loggerError = spyOn(logger, 'error');
|
|
|
|
builder
|
|
.run({
|
|
root: normalize('/root'),
|
|
projectType: 'application',
|
|
builder: '@nrwl/node:execute',
|
|
options: {
|
|
...testOptions,
|
|
waitUntilTargets: ['project1:target1', 'project2:target2']
|
|
}
|
|
})
|
|
.subscribe(e => {
|
|
expect(e.success).toEqual(false);
|
|
expect(loggerError).toHaveBeenCalled();
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|