diff --git a/src/hmr/hotModuleReplacement.js b/src/hmr/hotModuleReplacement.js
index 6d6611b1..18ac9047 100644
--- a/src/hmr/hotModuleReplacement.js
+++ b/src/hmr/hotModuleReplacement.js
@@ -160,6 +160,7 @@ function reloadStyle(src) {
if (url) {
updateCss(el, url);
+
loaded = true;
}
});
@@ -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;
}
diff --git a/test/HMR.test.js b/test/HMR.test.js
new file mode 100644
index 00000000..9c430a17
--- /dev/null
+++ b/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 = '';
+ document.body.innerHTML = '';
+ });
+
+ 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 =
+ '';
+
+ 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 =
+ '';
+
+ 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 =
+ '';
+
+ 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);
+ });
+});
diff --git a/test/__snapshots__/HMR.test.js.snap b/test/__snapshots__/HMR.test.js.snap
new file mode 100644
index 00000000..5f56ca94
--- /dev/null
+++ b/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`] = `""`;
+
+exports[`HMR should reloads with # link href 1`] = `"[HMR] css reload %s"`;
+
+exports[`HMR should reloads with # link href 2`] = `""`;
+
+exports[`HMR should reloads with link without href 1`] = `"[HMR] css reload %s"`;
+
+exports[`HMR should reloads with link without href 2`] = `""`;
+
+exports[`HMR should reloads with locals 1`] = `"[HMR] Detected local css modules. Reload all css"`;
+
+exports[`HMR should reloads with locals 2`] = `""`;
+
+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`] = `""`;
+
+exports[`HMR should reloads with reloadAll option 1`] = `"[HMR] Reload all css"`;
+
+exports[`HMR should reloads with reloadAll option 2`] = `""`;
+
+exports[`HMR should works 1`] = `"[HMR] css reload %s"`;
+
+exports[`HMR should works 2`] = `""`;
+
+exports[`HMR should works with multiple updates 1`] = `"[HMR] css reload %s"`;
+
+exports[`HMR should works with multiple updates 2`] = `""`;
+
+exports[`HMR should works with multiple updates 3`] = `""`;