Skip to content

Commit

Permalink
fix: hmr reload with invalid link url
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed May 24, 2019
1 parent ee9df43 commit 643545c
Show file tree
Hide file tree
Showing 3 changed files with 298 additions and 12 deletions.
15 changes: 3 additions & 12 deletions src/hmr/hotModuleReplacement.js
Expand Up @@ -160,6 +160,7 @@ function reloadStyle(src) {

if (url) {
updateCss(el, url);

loaded = true;
}
});
Expand All @@ -182,18 +183,8 @@ function reloadAll() {
function isUrlRequest(url) {
// An URL is not an request if

// 1. It's an absolute url
if (/^[a-z][a-z0-9+.-]*:/i.test(url)) {
return false;
}

// 2. It's a protocol-relative
if (/^\/\//.test(url)) {
return false;
}

// 3. Its a `#` link
if (/^#/.test(url)) {
// It is not http or https
if (!/^https?:/i.test(url)) {
return false;
}

Expand Down
260 changes: 260 additions & 0 deletions test/HMR.test.js
@@ -0,0 +1,260 @@
/* eslint-env browser */
/* eslint-disable no-console */

import hotModuleReplacement from '../src/hmr/hotModuleReplacement';

function getLoadEvent() {
const event = document.createEvent('Event');

event.initEvent('load', false, false);

return event;
}

function getErrorEvent() {
const event = document.createEvent('Event');

event.initEvent('error', false, false);

return event;
}

describe('HMR', () => {
let consoleMock = null;

beforeEach(() => {
consoleMock = jest.spyOn(console, 'log').mockImplementation(() => () => {});

jest.spyOn(Date, 'now').mockImplementation(() => 1479427200000);

document.head.innerHTML = '<link rel="stylesheet" href="/dist/main.css" />';
document.body.innerHTML = '<script src="/dist/main.js"></script>';
});

afterEach(() => {
consoleMock.mockClear();
});

it('should works', (done) => {
const update = hotModuleReplacement('./src/style.css', {});

update();

setTimeout(() => {
expect(console.log.mock.calls[0][0]).toMatchSnapshot();

const links = Array.prototype.slice.call(
document.querySelectorAll('link')
);

expect(links[0].visited).toBe(true);
expect(document.head.innerHTML).toMatchSnapshot();

links[1].dispatchEvent(getLoadEvent());

expect(links[1].isLoaded).toBe(true);

done();
}, 100);
});

it('should works with multiple updates', (done) => {
const update = hotModuleReplacement('./src/style.css', {});

update();

setTimeout(() => {
expect(console.log.mock.calls[0][0]).toMatchSnapshot();

const links = Array.prototype.slice.call(
document.querySelectorAll('link')
);

expect(links[0].visited).toBe(true);
expect(document.head.innerHTML).toMatchSnapshot();

links[1].dispatchEvent(getLoadEvent());

expect(links[1].isLoaded).toBe(true);

jest.spyOn(Date, 'now').mockImplementation(() => 1479427200001);

const update2 = hotModuleReplacement('./src/style.css', {});

update2();

setTimeout(() => {
const links2 = Array.prototype.slice.call(
document.querySelectorAll('link')
);

expect(links2[0].visited).toBe(true);
expect(links2[0].isLoaded).toBe(true);
expect(document.head.innerHTML).toMatchSnapshot();

links2[1].dispatchEvent(getLoadEvent());

expect(links2[1].isLoaded).toBe(true);

done();
}, 100);
}, 100);
});

it('should reloads with locals', (done) => {
const update = hotModuleReplacement('./src/style.css', {
locals: { foo: 'bar' },
});

update();

setTimeout(() => {
expect(console.log.mock.calls[0][0]).toMatchSnapshot();

const links = Array.prototype.slice.call(
document.querySelectorAll('link')
);

expect(links[0].visited).toBe(true);
expect(document.head.innerHTML).toMatchSnapshot();

links[1].dispatchEvent(getLoadEvent());

expect(links[1].isLoaded).toBe(true);

done();
}, 100);
});

it('should reloads with reloadAll option', (done) => {
const update = hotModuleReplacement('./src/style.css', {
reloadAll: true,
});

update();

setTimeout(() => {
expect(console.log.mock.calls[0][0]).toMatchSnapshot();

const links = Array.prototype.slice.call(
document.querySelectorAll('link')
);

expect(links[0].visited).toBe(true);
expect(document.head.innerHTML).toMatchSnapshot();

links[1].dispatchEvent(getLoadEvent());

expect(links[1].isLoaded).toBe(true);

done();
}, 100);
});

it('should reloads with non http/https link href', (done) => {
document.head.innerHTML =
'<link rel="stylesheet" href="/dist/main.css" /><link rel="shortcut icon" href="data:;base64,=" />';

const update = hotModuleReplacement('./src/style.css', {});

update();

setTimeout(() => {
expect(console.log.mock.calls[0][0]).toMatchSnapshot();

const links = Array.prototype.slice.call(
document.querySelectorAll('link')
);

expect(links[0].visited).toBe(true);
expect(document.head.innerHTML).toMatchSnapshot();

expect(links[1].visited).toBeUndefined();

links[2].dispatchEvent(getLoadEvent());

expect(links[2].isLoaded).toBe(true);

done();
}, 100);
});

it('should reloads with # link href', (done) => {
document.head.innerHTML =
'<link rel="stylesheet" href="/dist/main.css" /><link rel="shortcut icon" href="#href" />';

const update = hotModuleReplacement('./src/style.css', {});

update();

setTimeout(() => {
expect(console.log.mock.calls[0][0]).toMatchSnapshot();

const links = Array.prototype.slice.call(
document.querySelectorAll('link')
);

expect(links[0].visited).toBe(true);
expect(document.head.innerHTML).toMatchSnapshot();

expect(links[1].visited).toBeUndefined();

links[2].dispatchEvent(getLoadEvent());

expect(links[2].isLoaded).toBe(true);

done();
}, 100);
});

it('should reloads with link without href', (done) => {
document.head.innerHTML =
'<link rel="stylesheet" href="/dist/main.css" /><link rel="shortcut icon" />';

const update = hotModuleReplacement('./src/style.css', {});

update();

setTimeout(() => {
expect(console.log.mock.calls[0][0]).toMatchSnapshot();

const links = Array.prototype.slice.call(
document.querySelectorAll('link')
);

expect(links[0].visited).toBe(true);
expect(document.head.innerHTML).toMatchSnapshot();

expect(links[1].visited).toBeUndefined();

links[2].dispatchEvent(getLoadEvent());

expect(links[2].isLoaded).toBe(true);

done();
}, 100);
});

it('should handle error event', (done) => {
const update = hotModuleReplacement('./src/style.css', {});

update();

setTimeout(() => {
expect(console.log.mock.calls[0][0]).toMatchSnapshot();

const links = Array.prototype.slice.call(
document.querySelectorAll('link')
);

expect(links[0].visited).toBe(true);
expect(document.head.innerHTML).toMatchSnapshot();

links[1].dispatchEvent(getErrorEvent());

expect(links[1].isLoaded).toBe(true);

done();
}, 100);
});
});
35 changes: 35 additions & 0 deletions test/__snapshots__/HMR.test.js.snap
@@ -0,0 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`HMR should handle error event 1`] = `"[HMR] css reload %s"`;

exports[`HMR should handle error event 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should reloads with # link href 1`] = `"[HMR] css reload %s"`;
exports[`HMR should reloads with # link href 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"shortcut icon\\" href=\\"#href\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should reloads with link without href 1`] = `"[HMR] css reload %s"`;
exports[`HMR should reloads with link without href 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"shortcut icon\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should reloads with locals 1`] = `"[HMR] Detected local css modules. Reload all css"`;
exports[`HMR should reloads with locals 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should reloads with non http/https link href 1`] = `"[HMR] css reload %s"`;
exports[`HMR should reloads with non http/https link href 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"shortcut icon\\" href=\\"data:;base64,=\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should reloads with reloadAll option 1`] = `"[HMR] Reload all css"`;
exports[`HMR should reloads with reloadAll option 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should works 1`] = `"[HMR] css reload %s"`;
exports[`HMR should works 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should works with multiple updates 1`] = `"[HMR] css reload %s"`;
exports[`HMR should works with multiple updates 2`] = `"<link rel=\\"stylesheet\\" href=\\"/dist/main.css\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\">"`;
exports[`HMR should works with multiple updates 3`] = `"<link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200000\\"><link rel=\\"stylesheet\\" href=\\"http://localhost/dist/main.css?1479427200001\\">"`;

0 comments on commit 643545c

Please sign in to comment.