Skip to content

Commit

Permalink
Fix: Spawn webserver in separate process
Browse files Browse the repository at this point in the history
  • Loading branch information
molant committed Jan 25, 2019
1 parent 39eeb44 commit 3d6e7cb
Show file tree
Hide file tree
Showing 23 changed files with 947 additions and 925 deletions.
53 changes: 22 additions & 31 deletions packages/connector-chrome/tests/collect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { URL } from 'url';
import * as sinon from 'sinon';
import anyTest, { TestInterface, ExecutionContext } from 'ava';

import { createServer, ServerConfiguration, Server } from '@hint/utils-create-server';
import { IConnector, Events, IConnectorConstructor } from 'hint/dist/src/lib/types';
import { Server, ServerConfiguration } from '@hint/utils-create-server';
import { Events, IConnector, IConnectorConstructor } from 'hint/dist/src/lib/types';
import generateHTMLPage from 'hint/dist/src/lib/utils/misc/generate-html-page';
import ChromeConnector from '../src/connector';
import { Engine } from 'hint';
Expand All @@ -19,50 +19,41 @@ type CollectContext = {
engine: Engine<Events>;
engineEmitSpy: sinon.SinonSpy;
engineEmitAsyncSpy: sinon.SinonSpy;
server: Server;
};

const test = anyTest as TestInterface<CollectContext>;

test.beforeEach(async (t) => {
test.beforeEach((t) => {
const engine: Engine<Events> = {
emit(): boolean {
return false;
},
async emitAsync(): Promise<any> { }
} as any;

const server: Server = createServer();

await server.start();

t.context.engineEmitSpy = sinon.spy(engine, 'emit');
t.context.engineEmitAsyncSpy = sinon.spy(engine, 'emitAsync');

t.context.server = server;
t.context.engine = engine;
});

test.afterEach.always((t) => {
t.context.server.stop();
t.context.engineEmitSpy.restore();
t.context.engineEmitAsyncSpy.restore();
});

const pathToFaviconInDir = path.join(__dirname, './fixtures/common/favicon.ico');
const pathToFaviconInLinkElement = path.join(__dirname, './fixtures/common/favicon-32x32.png');

const runTest = async (t: ExecutionContext<CollectContext>, ConnectorConstructor: IConnectorConstructor, serverConfig?: ServerConfiguration) => {
const runTest = async (t: ExecutionContext<CollectContext>, ConnectorConstructor: IConnectorConstructor, serverConfig: ServerConfiguration): Promise<Server> => {
const server = await Server.create({ configuration: serverConfig });
const { engine } = t.context;
const connector: IConnector = new ConnectorConstructor(engine, {});
const server = t.context.server;

if (serverConfig) {
server.configure(serverConfig);
}

await connector.collect(new URL(`http://localhost:${server.port}/`));
await connector.close();

return server;
};

test(`[${name}] The HTML is downloaded and the right event emitted`, async (t) => {
Expand All @@ -74,65 +65,65 @@ test(`[${name}] The HTML is downloaded and the right event emitted`, async (t) =
});

test(`[${name}] Favicon is present in a 'link' element with 'rel' attribute set to 'icon' `, async (t) => {
const faviconInLinkElementDir = `http://localhost:${t.context.server.port}/images/favicon-32x32.png`;
const faviconInLinkElementDir = `http://localhost/images/favicon-32x32.png`;
const serverConfig: ServerConfiguration = {
'/': generateHTMLPage(`<link rel="icon" type="image/png" href="/images/favicon-32x32.png" sizes="32x32">`),
'/images/favicon-32x32.png': fs.readFileSync(pathToFaviconInLinkElement)
};

await runTest(t, ChromeConnector, serverConfig);
const server = await runTest(t, ChromeConnector, serverConfig);

t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').callCount, 1);
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, faviconInLinkElementDir);
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, Server.updateLocalhost(faviconInLinkElementDir, server.port));

});

test(`[${name}] Favicon is present in the root directory`, async (t) => {
const faviconInRootDir = `http://localhost:${t.context.server.port}/favicon.ico`;
const faviconInRootDir = `http://localhost/favicon.ico`;
const serverConfig: ServerConfiguration = { '/favicon.ico': fs.readFileSync(pathToFaviconInDir) };

await runTest(t, ChromeConnector, serverConfig);
const server = await runTest(t, ChromeConnector, serverConfig);

t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').callCount, 1);
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, faviconInRootDir);
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, Server.updateLocalhost(faviconInRootDir, server.port));
});

test(`[${name}] Favicon is present in both the root directory and the 'link' element`, async (t) => {
const faviconInLinkElementDir = `http://localhost:${t.context.server.port}/images/favicon-32x32.png`;
const faviconInLinkElementDir = `http://localhost/images/favicon-32x32.png`;
const serverConfig: ServerConfiguration = {
'/': generateHTMLPage(`<link rel="icon" type="image/png" href="/images/favicon-32x32.png" sizes="32x32">`),
'/favicon.ico': fs.readFileSync(pathToFaviconInDir),
'/images/favicon-32x32.png': fs.readFileSync(pathToFaviconInLinkElement)
};

await runTest(t, ChromeConnector, serverConfig);
const server = await runTest(t, ChromeConnector, serverConfig);

t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').callCount, 1);
// Should load favicon from the link element if it exists
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, faviconInLinkElementDir);
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, Server.updateLocalhost(faviconInLinkElementDir, server.port));
});

test(`[${name}] Favicon is present in both the root directory and the 'link' element, but the 'link' element has empty 'href'`, async (t) => {
const faviconInRootDir = `http://localhost:${t.context.server.port}/favicon.ico`;
const faviconInRootDir = `http://localhost/favicon.ico`;
const serverConfig: ServerConfiguration = {
'/': generateHTMLPage(`<link rel="icon" type="image/png" href="" sizes="32x32">`),
'/favicon.ico': fs.readFileSync(pathToFaviconInDir)
};

await runTest(t, ChromeConnector, serverConfig);
const server = await runTest(t, ChromeConnector, serverConfig);

t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').callCount, 1);
// Should load favicon from the root even though the link element exists because 'href' is empty.
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, faviconInRootDir);
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, Server.updateLocalhost(faviconInRootDir, server.port));
});

test(`[${name}] Favicon is not present in either the root directory or the 'link' element`, async (t) => {
const faviconInRootDir = `http://localhost:${t.context.server.port}/favicon.ico`;
const faviconInRootDir = `http://localhost/favicon.ico`;
const serverConfig: ServerConfiguration = { '/': generateHTMLPage() };

await runTest(t, ChromeConnector, serverConfig);
const server = await runTest(t, ChromeConnector, serverConfig);

// Requests to `/favicon.ico` are always sent when favicon doesn't exist as a `link` tag in the html.
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').callCount, 1);
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, faviconInRootDir);
t.is(t.context.engineEmitAsyncSpy.withArgs('fetch::end::image').args[0][1].request.url, Server.updateLocalhost(faviconInRootDir, server.port));
});
40 changes: 7 additions & 33 deletions packages/connector-chrome/tests/evaluate.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import { URL } from 'url';

import anyTest, { TestInterface } from 'ava';
import test from 'ava';

import { createServer, Server } from '@hint/utils-create-server';
import { Server } from '@hint/utils-create-server';
import generateHTMLPage from 'hint/dist/src/lib/utils/misc/generate-html-page';
import { IConnector, Events } from 'hint/dist/src/lib/types';
import { Engine } from 'hint';

import ChromeConnector from '../src/connector';

type EvaluateContext = {
connector: IConnector;
engine: Engine<Events>;
server: Server;
};

const test = anyTest as TestInterface<EvaluateContext>;

const name: string = 'chrome';

const scripts = [
Expand Down Expand Up @@ -55,7 +47,7 @@ const scripts = [
}
];

test.beforeEach(async (t) => {
test(`[${name}] Evaluate JavaScript`, async (t) => {
const engine: Engine<Events> = {
emit(): boolean {
return false;
Expand All @@ -64,29 +56,8 @@ test.beforeEach(async (t) => {
timeout: 10000
} as any;

const server = createServer();

await server.start();

t.context.engine = engine;
t.context.server = server;
});

test.afterEach.always(async (t) => {
t.context.server.stop();
await t.context.connector.close();
});

test(`[${name}] Evaluate JavaScript`, async (t) => {
const { engine } = t.context;
const server = await Server.create({ configuration: generateHTMLPage('', '') });
const connector: IConnector = new ChromeConnector(engine, {});
const server = t.context.server;

t.context.connector = connector;

t.plan(scripts.length);

server.configure(generateHTMLPage('', ''));

await connector.collect(new URL(`http://localhost:${server.port}/`));

Expand Down Expand Up @@ -123,4 +94,7 @@ test(`[${name}] Evaluate JavaScript`, async (t) => {
}
}
}

await connector.close();
await server.stop();
});
89 changes: 28 additions & 61 deletions packages/connector-chrome/tests/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import * as fs from 'fs';
import * as path from 'path';
import { URL } from 'url';

import { map, reduce, groupBy, every } from 'lodash';
import { groupBy, every } from 'lodash';
import * as sinon from 'sinon';
import anyTest, { TestInterface } from 'ava';
import { createServer, Server } from '@hint/utils-create-server';
import { Server } from '@hint/utils-create-server';
import { IConnector, Events } from 'hint/dist/src/lib/types';
import { Engine } from 'hint';

Expand All @@ -23,7 +23,6 @@ type EventsContext = {
engine: Engine<Events>;
engineEmitSpy: sinon.SinonSpy;
engineEmitAsyncSpy: sinon.SinonSpy;
server: Server;
};

const test = anyTest as TestInterface<EventsContext>;
Expand Down Expand Up @@ -202,7 +201,7 @@ const validEvent = (eventsToSearch: any[], expectedEvent: any) => {
return originalSize !== eventsToSearch.length;
};

test.beforeEach(async (t) => {
test.beforeEach((t) => {
const engine: Engine<Events> = {
emit(): boolean {
return false;
Expand All @@ -213,81 +212,46 @@ test.beforeEach(async (t) => {
t.context.engineEmitAsyncSpy = sinon.spy(engine, 'emitAsync');
t.context.engineEmitSpy = sinon.spy(engine, 'emit');

const server: Server = createServer();

await server.start();

t.context.engine = engine;
t.context.server = server;
});

test.afterEach.always(async (t) => {
t.context.engineEmitAsyncSpy.restore();
t.context.engineEmitSpy.restore();
t.context.server.stop();
await t.context.connector!.close();
});

/**
* Updates all references to localhost to use the right port for the current instance.
*
* This does a deep search in all the object properties.
*/
const updateLocalhost = (content: any, port: any): any => {
if (typeof content === 'string') {
return content.replace(/localhost\//g, `localhost:${port}/`);
}

if (typeof content === 'number' || !content) {
return content;
}

if (Array.isArray(content)) {
const transformed = map(content, (value) => {
return updateLocalhost(value, port);
});

return transformed;
}

const transformed = reduce(content, (obj: any, value, key) => {
obj[key] = updateLocalhost(value, port);

return obj;
}, {});

return transformed;
};

test(`[${name}] Events`, async (t) => {
const { engine } = t.context;
const connector: IConnector = new ChromeConnector(engine, {});
const server = t.context.server;


t.context.connector = connector;

server.configure({
'/': updateLocalhost(fs.readFileSync(path.join(__dirname, './fixtures/common/index.html'), 'utf8'), server.port),
'/nellie.png': { content: fs.readFileSync(path.join(__dirname, './fixtures/common/nellie.png')) },
'/script.js': { content: '' },
'/script2.js': {
content: 'script.js',
status: 302
},
'/script3.js': {
content: 'script2.js',
status: 302
},
'/script4.js': {
content: 'script4.js',
status: 404
},
'/script5.js': null,
'/style.css': { content: '' }
const server = await Server.create({
configuration: {
'/': fs.readFileSync(path.join(__dirname, './fixtures/common/index.html'), 'utf-8'),
'/nellie.png': { content: fs.readFileSync(path.join(__dirname, './fixtures/common/nellie.png')) },
'/script.js': { content: '' },
'/script2.js': {
content: 'script.js',
status: 302
},
'/script3.js': {
content: 'script2.js',
status: 302
},
'/script4.js': {
content: 'script4.js',
status: 404
},
'/script5.js': null,
'/style.css': { content: '' }
}
});

const pendingEvents: any[] = events.map((event) => {
return updateLocalhost(event, server.port);
return Server.updateLocalhost(event, server.port);
});

await connector.collect(new URL(`http://localhost:${server.port}/`));
Expand Down Expand Up @@ -316,4 +280,7 @@ test(`[${name}] Events`, async (t) => {
pendingEvents.forEach((event) => {
t.true(validEvent(invokes, event), `Event ${event[0]}/${event[1].resource} has the same properties`);
});

await connector.close();
await server.stop();
});
Loading

0 comments on commit 3d6e7cb

Please sign in to comment.