1 | const deepAssign = require('deep-assign');
|
2 |
|
3 | const log = require('loglevel');
|
4 | const unmirror = require('chrome-unmirror');
|
5 |
|
6 | const createInstance = require('./lib/instance');
|
7 | const connectClient = require('./lib/client');
|
8 | const EventBus = require('./lib/event-bus');
|
9 | const chromeOut = require('./lib/chrome-out');
|
10 | const network = require('./lib/network');
|
11 |
|
12 | process.on('unhandledRejection', (error) => {
|
13 | const { error: stderr } = console;
|
14 |
|
15 | stderr('Promise Rejection: ', error);
|
16 | });
|
17 |
|
18 | class MochaChrome {
|
19 | constructor(options) {
|
20 |
|
21 | options = deepAssign(
|
22 | {
|
23 | chromeFlags: [],
|
24 | loadTimeout: 1000,
|
25 | logLevel: 'error',
|
26 | ignoreExceptions: false,
|
27 | ignoreConsole: false,
|
28 | ignoreResourceErrors: false,
|
29 | mocha: {
|
30 | reporter: 'spec',
|
31 | ui: 'bdd',
|
32 | useColors: true
|
33 | }
|
34 | },
|
35 | options
|
36 | );
|
37 |
|
38 | log.setDefaultLevel('error');
|
39 | log.setLevel(options.logLevel);
|
40 |
|
41 | const bus = new EventBus(log);
|
42 |
|
43 | if (!options.url) {
|
44 | this.fail('`options.url` must be specified to run tests');
|
45 | }
|
46 |
|
47 | bus.on('ready', () => {
|
48 | this.client.Runtime.evaluate({ expression: `mocha.setup({ reporter: 'spec' })` });
|
49 | });
|
50 |
|
51 | bus.on('mocha', (content) => {
|
52 | process.stdout.write(content);
|
53 | });
|
54 |
|
55 | bus.on('width', () => {
|
56 |
|
57 | const columns = (parseInt(process.env.COLUMNS || process.stdout.columns, 10) * 0.75) | 0;
|
58 | const expression = `Mocha.reporters.Base.window.width = ${columns};`;
|
59 |
|
60 | this.client.Runtime.evaluate({ expression });
|
61 | });
|
62 |
|
63 | bus.on('config', () => {
|
64 | const config = JSON.stringify(options.mocha);
|
65 | const expression = `mocha.setup(${config})`;
|
66 |
|
67 | this.client.Runtime.evaluate({ expression });
|
68 | });
|
69 |
|
70 | bus.on('started', (tests) => {
|
71 | this.started = true;
|
72 | log.info(`Test Run Started - Running ${tests} Tests\n`);
|
73 |
|
74 | if (tests === 0) {
|
75 | this.fail('mocha.run() was called with no tests');
|
76 | }
|
77 | });
|
78 |
|
79 | bus.on('ended', (stats) => {
|
80 | this.ended = true;
|
81 | this.exit(stats.failures);
|
82 | });
|
83 |
|
84 | bus.on('resourceFailed', () => {
|
85 | this.loadError = true;
|
86 | });
|
87 |
|
88 | this.bus = bus;
|
89 | this.options = options;
|
90 | this.loadError = false;
|
91 | }
|
92 |
|
93 | async connect() {
|
94 | const instance = await createInstance(log, this.options);
|
95 | const client = await connectClient(instance, log, this.options);
|
96 | const { DOMStorage, Network, Runtime } = client;
|
97 |
|
98 | if (!client) {
|
99 | this.fail('CDP Client could not connect');
|
100 | return;
|
101 | }
|
102 |
|
103 | this.bus.watch(DOMStorage);
|
104 |
|
105 | chromeOut(log, this.options, Runtime);
|
106 | network(this.bus, log, Network, this.options.ignoreResourceErrors);
|
107 |
|
108 | this.client = client;
|
109 | this.instance = instance;
|
110 | }
|
111 |
|
112 | async run() {
|
113 | this.client.Page.loadEventFired(() => {
|
114 | if (this.closed) {
|
115 | return;
|
116 | }
|
117 |
|
118 | if (this.loadError) {
|
119 | this.fail(`Failed to load the page. Check the url: ${this.options.url}`);
|
120 | return;
|
121 | }
|
122 |
|
123 | setTimeout(async () => {
|
124 | if (this.closed) {
|
125 | return;
|
126 | }
|
127 |
|
128 | const expression = '(function () { return !!window.mocha; })()';
|
129 | const res = await this.client.Runtime.evaluate({ expression });
|
130 |
|
131 | if (!unmirror(res.result)) {
|
132 | this.fail(
|
133 | `mocha was not found in the page within ${this.options.loadTimeout}ms of the page loading.`
|
134 | );
|
135 | }
|
136 |
|
137 | if (!this.started) {
|
138 | this.fail(
|
139 | `mocha.run() was not called within ${this.options.loadTimeout}ms of the page loading.`
|
140 | );
|
141 | }
|
142 | }, this.options.loadTimeout);
|
143 | });
|
144 |
|
145 | await this.client.Page.navigate({ url: this.options.url });
|
146 | }
|
147 |
|
148 | on(name, fn) {
|
149 | this.bus.on(name, fn);
|
150 | }
|
151 |
|
152 | fail(message) {
|
153 | log.error('Mocha-Chrome Failed:', message || '');
|
154 |
|
155 | if (this.bus) {
|
156 | this.bus.emit('failure', message);
|
157 | }
|
158 |
|
159 | this.exit(1);
|
160 | }
|
161 |
|
162 | async exit() {
|
163 | this.closed = true;
|
164 | await this.client.close();
|
165 | await this.instance.kill();
|
166 |
|
167 | this.bus.emit('exit', 1);
|
168 | }
|
169 | }
|
170 |
|
171 | module.exports = MochaChrome;
|