/** @jest-environment jsdom */ /* 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 work reload all css", (done) => { const update = hotModuleReplacement("./src/style.css", { filename: "unreload_url", }); 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(); links[1].dispatchEvent(getLoadEvent()); expect(links[1].isLoaded).toBe(true); expect(links[2].visited).toBeUndefined(); 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(); links[1].dispatchEvent(getLoadEvent()); expect(links[1].isLoaded).toBe(true); expect(links[2].visited).toBeUndefined(); 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(); links[1].dispatchEvent(getLoadEvent()); expect(links[1].isLoaded).toBe(true); expect(links[2].visited).toBeUndefined(); done(); }, 100); }); it("should reloads with absolute remove url", (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(); links[1].dispatchEvent(getLoadEvent()); expect(links[1].isLoaded).toBe(true); expect(links[2].visited).toBeUndefined(); done(); }, 100); }); it("should reloads with browser extension protocol", (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(); links[1].dispatchEvent(getLoadEvent()); expect(links[1].isLoaded).toBe(true); expect(links[2].visited).toBeUndefined(); done(); }, 100); }); it("should reloads with non-file script in the end of page", (done) => { document.body.appendChild(document.createElement("script")); const update = hotModuleReplacement("./src/non_file_styles.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 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); }); it("should not remove old link when new link is loaded twice", (done) => { const link = document.createElement("link"); link.innerHTML = ''; document.head.appendChild(link); document.head.removeChild = jest.fn(); const update = hotModuleReplacement("./dist/main.css", {}); update(); setTimeout(() => { const links = Array.prototype.slice.call( document.querySelectorAll("link") ); links[1].dispatchEvent(getLoadEvent()); links[1].dispatchEvent(getLoadEvent()); expect(document.head.removeChild).toHaveBeenCalledTimes(1); done(); }, 100); }); });