Skip to content

Commit d31439e

Browse files
Merge pull request #3200 from cdr/jsjoeio/add-test-browser-register
feat(testing): add tests for registerServiceWorker
2 parents 5ad8e68 + 83746c8 commit d31439e

File tree

3 files changed

+96
-3
lines changed

3 files changed

+96
-3
lines changed

src/browser/register.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import "./pages/error.css"
44
import "./pages/global.css"
55
import "./pages/login.css"
66

7-
async function registerServiceWorker(): Promise<void> {
7+
export async function registerServiceWorker(): Promise<void> {
88
const options = getOptions()
99
const path = normalize(`${options.csStaticBase}/dist/serviceWorker.js`)
1010
try {
1111
await navigator.serviceWorker.register(path, {
12-
scope: (options.base ?? "") + "/",
12+
scope: options.base + "/",
1313
})
1414
console.log("[Service Worker] registered")
1515
} catch (error) {

test/unit/register.test.ts

+93
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { JSDOM } from "jsdom"
22
import { loggerModule } from "../utils/helpers"
3+
import { registerServiceWorker } from "../../src/browser/register"
4+
import { LocationLike } from "./util.test"
35

46
describe("register", () => {
57
describe("when navigator and serviceWorker are defined", () => {
@@ -37,6 +39,12 @@ describe("register", () => {
3739
global.navigator = (undefined as unknown) as Navigator & typeof globalThis
3840
global.location = (undefined as unknown) as Location & typeof globalThis
3941
})
42+
it("test should have access to browser globals from beforeAll", () => {
43+
expect(typeof global.window).not.toBeFalsy()
44+
expect(typeof global.document).not.toBeFalsy()
45+
expect(typeof global.navigator).not.toBeFalsy()
46+
expect(typeof global.location).not.toBeFalsy()
47+
})
4048

4149
it("should register a ServiceWorker", () => {
4250
// Load service worker like you would in the browser
@@ -84,4 +92,89 @@ describe("register", () => {
8492
expect(spy).toHaveBeenCalledWith("[Service Worker] navigator is undefined")
8593
})
8694
})
95+
describe("registerServiceWorker", () => {
96+
let serviceWorkerPath: string
97+
let serviceWorkerScope: string
98+
const mockFn = jest.fn((path: string, options: { scope: string }) => {
99+
serviceWorkerPath = path
100+
serviceWorkerScope = options.scope
101+
return undefined
102+
})
103+
104+
beforeAll(() => {
105+
const location: LocationLike = {
106+
pathname: "",
107+
origin: "http://localhost:8080",
108+
}
109+
const { window } = new JSDOM()
110+
global.window = (window as unknown) as Window & typeof globalThis
111+
global.document = window.document
112+
global.navigator = window.navigator
113+
global.location = location as Location
114+
115+
Object.defineProperty(global.navigator, "serviceWorker", {
116+
value: {
117+
register: mockFn,
118+
},
119+
})
120+
})
121+
122+
afterEach(() => {
123+
mockFn.mockClear()
124+
jest.resetModules()
125+
})
126+
127+
afterAll(() => {
128+
jest.restoreAllMocks()
129+
130+
// We don't want these to stay around because it can affect other tests
131+
global.window = (undefined as unknown) as Window & typeof globalThis
132+
global.document = (undefined as unknown) as Document & typeof globalThis
133+
global.navigator = (undefined as unknown) as Navigator & typeof globalThis
134+
global.location = (undefined as unknown) as Location & typeof globalThis
135+
})
136+
it("should register when options.base is undefined", async () => {
137+
// Mock getElementById
138+
const csStaticBasePath = "/static/development/Users/jp/Dev/code-server"
139+
const spy = jest.spyOn(document, "getElementById")
140+
// Create a fake element and set the attribute
141+
const mockElement = document.createElement("div")
142+
mockElement.id = "coder-options"
143+
mockElement.setAttribute(
144+
"data-settings",
145+
`{"csStaticBase":"${csStaticBasePath}","logLevel":2,"disableTelemetry":false,"disableUpdateCheck":false}`,
146+
)
147+
// Return mockElement from the spy
148+
// this way, when we call "getElementById"
149+
// it returns the element
150+
spy.mockImplementation(() => mockElement)
151+
152+
await registerServiceWorker()
153+
154+
expect(mockFn).toBeCalled()
155+
expect(serviceWorkerPath).toMatch(`${csStaticBasePath}/dist/serviceWorker.js`)
156+
expect(serviceWorkerScope).toMatch("/")
157+
})
158+
it("should register when options.base is defined", async () => {
159+
const csStaticBasePath = "/static/development/Users/jp/Dev/code-server"
160+
const spy = jest.spyOn(document, "getElementById")
161+
// Create a fake element and set the attribute
162+
const mockElement = document.createElement("div")
163+
mockElement.id = "coder-options"
164+
mockElement.setAttribute(
165+
"data-settings",
166+
`{"base":"proxy/","csStaticBase":"${csStaticBasePath}","logLevel":2,"disableTelemetry":false,"disableUpdateCheck":false}`,
167+
)
168+
// Return mockElement from the spy
169+
// this way, when we call "getElementById"
170+
// it returns the element
171+
spy.mockImplementation(() => mockElement)
172+
173+
await registerServiceWorker()
174+
175+
expect(mockFn).toBeCalled()
176+
expect(serviceWorkerPath).toMatch(`/dist/serviceWorker.js`)
177+
expect(serviceWorkerScope).toMatch("/")
178+
})
179+
})
87180
})

test/unit/util.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { loggerModule } from "../utils/helpers"
1616
const dom = new JSDOM()
1717
global.document = dom.window.document
1818

19-
type LocationLike = Pick<Location, "pathname" | "origin">
19+
export type LocationLike = Pick<Location, "pathname" | "origin">
2020

2121
// jest.mock is hoisted above the imports so we must use `require` here.
2222
jest.mock("@coder/logger", () => require("../utils/helpers").loggerModule)

0 commit comments

Comments
 (0)