Skip to content

feat(testing): add tests for registerServiceWorker #3200

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/browser/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import "./pages/error.css"
import "./pages/global.css"
import "./pages/login.css"

async function registerServiceWorker(): Promise<void> {
export async function registerServiceWorker(): Promise<void> {
const options = getOptions()
const path = normalize(`${options.csStaticBase}/dist/serviceWorker.js`)
try {
await navigator.serviceWorker.register(path, {
scope: (options.base ?? "") + "/",
scope: options.base + "/",
Copy link
Contributor Author

@jsjoeio jsjoeio Apr 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TLDR: options.base will always return a string. No need to use nullish coalescing operator

See commit message for explanation: 67dbb36

})
console.log("[Service Worker] registered")
} catch (error) {
Expand Down
93 changes: 93 additions & 0 deletions test/unit/register.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { JSDOM } from "jsdom"
import { loggerModule } from "../utils/helpers"
import { registerServiceWorker } from "../../src/browser/register"
import { LocationLike } from "./util.test"

describe("register", () => {
describe("when navigator and serviceWorker are defined", () => {
Expand Down Expand Up @@ -37,6 +39,12 @@ describe("register", () => {
global.navigator = (undefined as unknown) as Navigator & typeof globalThis
global.location = (undefined as unknown) as Location & typeof globalThis
})
it("test should have access to browser globals from beforeAll", () => {
expect(typeof global.window).not.toBeFalsy()
expect(typeof global.document).not.toBeFalsy()
expect(typeof global.navigator).not.toBeFalsy()
expect(typeof global.location).not.toBeFalsy()
})

it("should register a ServiceWorker", () => {
// Load service worker like you would in the browser
Expand Down Expand Up @@ -84,4 +92,89 @@ describe("register", () => {
expect(spy).toHaveBeenCalledWith("[Service Worker] navigator is undefined")
})
})
describe("registerServiceWorker", () => {
let serviceWorkerPath: string
let serviceWorkerScope: string
const mockFn = jest.fn((path: string, options: { scope: string }) => {
serviceWorkerPath = path
serviceWorkerScope = options.scope
return undefined
})

beforeAll(() => {
const location: LocationLike = {
pathname: "",
origin: "http://localhost:8080",
}
const { window } = new JSDOM()
global.window = (window as unknown) as Window & typeof globalThis
global.document = window.document
global.navigator = window.navigator
global.location = location as Location

Object.defineProperty(global.navigator, "serviceWorker", {
value: {
register: mockFn,
},
})
})

afterEach(() => {
mockFn.mockClear()
jest.resetModules()
})

afterAll(() => {
jest.restoreAllMocks()

// We don't want these to stay around because it can affect other tests
global.window = (undefined as unknown) as Window & typeof globalThis
global.document = (undefined as unknown) as Document & typeof globalThis
global.navigator = (undefined as unknown) as Navigator & typeof globalThis
global.location = (undefined as unknown) as Location & typeof globalThis
})
it("should register when options.base is undefined", async () => {
// Mock getElementById
const csStaticBasePath = "/static/development/Users/jp/Dev/code-server"
const spy = jest.spyOn(document, "getElementById")
// Create a fake element and set the attribute
const mockElement = document.createElement("div")
mockElement.id = "coder-options"
mockElement.setAttribute(
"data-settings",
`{"csStaticBase":"${csStaticBasePath}","logLevel":2,"disableTelemetry":false,"disableUpdateCheck":false}`,
)
// Return mockElement from the spy
// this way, when we call "getElementById"
// it returns the element
spy.mockImplementation(() => mockElement)

await registerServiceWorker()

expect(mockFn).toBeCalled()
expect(serviceWorkerPath).toMatch(`${csStaticBasePath}/dist/serviceWorker.js`)
expect(serviceWorkerScope).toMatch("/")
})
it("should register when options.base is defined", async () => {
const csStaticBasePath = "/static/development/Users/jp/Dev/code-server"
const spy = jest.spyOn(document, "getElementById")
// Create a fake element and set the attribute
const mockElement = document.createElement("div")
mockElement.id = "coder-options"
mockElement.setAttribute(
"data-settings",
`{"base":"proxy/","csStaticBase":"${csStaticBasePath}","logLevel":2,"disableTelemetry":false,"disableUpdateCheck":false}`,
)
// Return mockElement from the spy
// this way, when we call "getElementById"
// it returns the element
spy.mockImplementation(() => mockElement)

await registerServiceWorker()

expect(mockFn).toBeCalled()
expect(serviceWorkerPath).toMatch(`/dist/serviceWorker.js`)
expect(serviceWorkerScope).toMatch("/")
})
})
})
2 changes: 1 addition & 1 deletion test/unit/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { loggerModule } from "../utils/helpers"
const dom = new JSDOM()
global.document = dom.window.document

type LocationLike = Pick<Location, "pathname" | "origin">
export type LocationLike = Pick<Location, "pathname" | "origin">

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