diff --git a/src/apps/epic/static/apps/epic/role-select.js b/src/apps/epic/static/apps/epic/role-select.js
index bee2fd0..5e1a9ad 100644
--- a/src/apps/epic/static/apps/epic/role-select.js
+++ b/src/apps/epic/static/apps/epic/role-select.js
@@ -61,6 +61,18 @@ var RoleSelect = (function () {
.split(",").filter(function (r) { return r.trim() !== roleCode; }).join(",");
}
openFan();
+ } else {
+ // Place role card in tray grid and open the tray
+ var grid = document.getElementById("id_tray_grid");
+ if (grid) {
+ var trayCard = document.createElement("div");
+ trayCard.className = "tray-cell tray-role-card";
+ trayCard.dataset.role = roleCode;
+ grid.insertBefore(trayCard, grid.firstChild);
+ }
+ if (typeof Tray !== "undefined") {
+ Tray.open();
+ }
}
});
}
diff --git a/src/functional_tests/test_room_role_select.py b/src/functional_tests/test_room_role_select.py
index d4c9cf5..1c92b34 100644
--- a/src/functional_tests/test_room_role_select.py
+++ b/src/functional_tests/test_room_role_select.py
@@ -621,7 +621,6 @@ class RoleSelectTrayTest(FunctionalTest):
# T1 — Portrait, position 1: empty tray, card at row 1 col 1 #
# ------------------------------------------------------------------ #
- @unittest.skip("tray-open-on-role-select not yet implemented")
def test_portrait_first_role_card_enters_grid_position_zero(self):
"""Portrait, slot 1: after confirming a role, a .tray-role-card element
appears as the first child of #id_tray_grid (topmost-leftmost cell), and
@@ -673,7 +672,6 @@ class RoleSelectTrayTest(FunctionalTest):
# T2 — Portrait, position 2: col 1 full, 8th item overflows to col 2 #
# ------------------------------------------------------------------ #
- @unittest.skip("tray-open-on-role-select not yet implemented")
def test_portrait_second_card_prepended_pushes_eighth_item_to_col_2(self):
"""Portrait, slot 2: col 1 already holds slot 1's role card (position 0)
plus 7 tray-cells (positions 1-7), filling the column. After slot 2
@@ -697,18 +695,23 @@ class RoleSelectTrayTest(FunctionalTest):
self._select_role()
- # 1. New card is first child.
+ # 1. Wait for grid to grow (fetch .then() is async).
self.wait_for(
- lambda: self.browser.find_element(
- By.CSS_SELECTOR, "#id_tray_grid .tray-role-card:first-child"
+ lambda: self.assertEqual(
+ self.browser.execute_script(
+ "return document.getElementById('id_tray_grid').children.length"
+ ),
+ grid_before + 1,
)
)
+ grid_after = grid_before + 1
- # 2. Grid now has 10 items (one more than before).
- grid_after = self.browser.execute_script(
- "return document.getElementById('id_tray_grid').children.length"
- )
- self.assertEqual(grid_after, grid_before + 1)
+ # 2. New tray-role-card is the first child.
+ is_first = self.browser.execute_script("""
+ var card = document.querySelector('#id_tray_grid .tray-role-card');
+ return card !== null && card === card.parentElement.firstElementChild;
+ """)
+ self.assertTrue(is_first, "Newest role card should be first child")
# 3. The item now at position 8 (col 2, row 1) is a tray-cell —
# it was the 8th item in col 1 and has been displaced.
@@ -731,7 +734,6 @@ class RoleSelectTrayTest(FunctionalTest):
# T3 — Landscape, position 3: row 1 full, rightmost item enters row 2 #
# ------------------------------------------------------------------ #
- @unittest.skip("tray-open-on-role-select not yet implemented")
def test_landscape_third_card_at_bottom_left_rightmost_overflows_to_row_2(self):
"""Landscape, slot 3: row 1 (bottom, 8 cols) already holds 2 prior role
cards + 6 tray-cells. After slot 3 confirms, new card at position 0
@@ -759,18 +761,23 @@ class RoleSelectTrayTest(FunctionalTest):
self._select_role()
- # 1. New card is first child — bottommost-leftmost in landscape.
+ # 1. Wait for grid to grow (fetch .then() is async).
self.wait_for(
- lambda: self.browser.find_element(
- By.CSS_SELECTOR, "#id_tray_grid .tray-role-card:first-child"
+ lambda: self.assertEqual(
+ self.browser.execute_script(
+ "return document.getElementById('id_tray_grid').children.length"
+ ),
+ grid_before + 1,
)
)
+ grid_after = grid_before + 1
- # 2. Grid grew by exactly one item.
- grid_after = self.browser.execute_script(
- "return document.getElementById('id_tray_grid').children.length"
- )
- self.assertEqual(grid_after, grid_before + 1)
+ # 2. Newest tray-role-card is the first child — bottommost-leftmost in landscape.
+ is_first = self.browser.execute_script("""
+ var card = document.querySelector('#id_tray_grid .tray-role-card');
+ return card !== null && card === card.parentElement.firstElementChild;
+ """)
+ self.assertTrue(is_first, "Newest role card should be first child")
# 3. Item at position 8 (row 2, col 1) is a tray-cell — it was the
# rightmost item in row 1 (position 7) and has been displaced upward.
diff --git a/src/functional_tests/test_room_tray.py b/src/functional_tests/test_room_tray.py
index 01c7334..75a45f8 100644
--- a/src/functional_tests/test_room_tray.py
+++ b/src/functional_tests/test_room_tray.py
@@ -1,3 +1,5 @@
+import time
+
from django.test import tag
from selenium.webdriver.common.by import By
@@ -41,12 +43,14 @@ class TrayTest(FunctionalTest):
Applet.objects.get_or_create(slug="new-note", defaults={"name": "New Note"})
def _switch_to_landscape(self):
- """Recreate the browser at landscape dimensions (900×500) and wait
- until window.innerWidth > window.innerHeight confirms the CSS
- orientation media query will fire correctly."""
+ """Recreate the browser, navigate to about:blank, then resize to
+ 900×500 and wait until window.innerWidth > window.innerHeight confirms
+ the CSS orientation media query will fire correctly on the next page."""
self.browser.quit()
self.browser = self._make_browser(900, 500)
self.browser.get('about:blank')
+ self.browser.set_window_size(900, 500)
+ time.sleep(0.5) # allow Firefox to flush the resize before navigating
self.wait_for(lambda: self.assertTrue(
self.browser.execute_script(
'return window.innerWidth > window.innerHeight'
diff --git a/src/static/tests/LICENSE b/src/static/tests/LICENSE
deleted file mode 100644
index 3bbb511..0000000
--- a/src/static/tests/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-Copyright (c) 2008-2019 Pivotal Labs
-Copyright (c) 2008-2026 The Jasmine developers
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/static/tests/RoleSelectSpec.js b/src/static/tests/RoleSelectSpec.js
deleted file mode 100644
index ecf762d..0000000
--- a/src/static/tests/RoleSelectSpec.js
+++ /dev/null
@@ -1,385 +0,0 @@
-describe("RoleSelect", () => {
- let testDiv;
-
- beforeEach(() => {
- testDiv = document.createElement("div");
- testDiv.innerHTML = `
-
-
-
- `;
- document.body.appendChild(testDiv);
- window.fetch = jasmine.createSpy("fetch").and.returnValue(
- Promise.resolve({ ok: true })
- );
- // Default stub: auto-confirm so existing card-click tests pass unchanged.
- // The click-guard integration describe overrides this with a capturing spy.
- window.showGuard = (_anchor, _msg, onConfirm) => onConfirm && onConfirm();
- });
-
- afterEach(() => {
- RoleSelect.closeFan();
- testDiv.remove();
- delete window.showGuard;
- });
-
- // ------------------------------------------------------------------ //
- // openFan() //
- // ------------------------------------------------------------------ //
-
- describe("openFan()", () => {
- it("creates .role-select-backdrop in the DOM", () => {
- RoleSelect.openFan();
- expect(document.querySelector(".role-select-backdrop")).not.toBeNull();
- });
-
- it("creates #id_role_select inside the backdrop", () => {
- RoleSelect.openFan();
- expect(document.getElementById("id_role_select")).not.toBeNull();
- });
-
- it("renders exactly 6 .card elements", () => {
- RoleSelect.openFan();
- const cards = document.querySelectorAll("#id_role_select .card");
- expect(cards.length).toBe(6);
- });
-
- it("does not open a second backdrop if already open", () => {
- RoleSelect.openFan();
- RoleSelect.openFan();
- expect(document.querySelectorAll(".role-select-backdrop").length).toBe(1);
- });
- });
-
- // ------------------------------------------------------------------ //
- // closeFan() //
- // ------------------------------------------------------------------ //
-
- describe("closeFan()", () => {
- it("removes .role-select-backdrop from the DOM", () => {
- RoleSelect.openFan();
- RoleSelect.closeFan();
- expect(document.querySelector(".role-select-backdrop")).toBeNull();
- });
-
- it("removes #id_role_select from the DOM", () => {
- RoleSelect.openFan();
- RoleSelect.closeFan();
- expect(document.getElementById("id_role_select")).toBeNull();
- });
-
- it("does not throw if no fan is open", () => {
- expect(() => RoleSelect.closeFan()).not.toThrow();
- });
- });
-
- // ------------------------------------------------------------------ //
- // Card interactions //
- // ------------------------------------------------------------------ //
-
- describe("card interactions", () => {
- beforeEach(() => {
- RoleSelect.openFan();
- });
-
- it("mouseenter adds .flipped to the card", () => {
- const card = document.querySelector("#id_role_select .card");
- card.dispatchEvent(new MouseEvent("mouseenter"));
- expect(card.classList.contains("flipped")).toBe(true);
- });
-
- it("mouseleave removes .flipped from the card", () => {
- const card = document.querySelector("#id_role_select .card");
- card.dispatchEvent(new MouseEvent("mouseenter"));
- card.dispatchEvent(new MouseEvent("mouseleave"));
- expect(card.classList.contains("flipped")).toBe(false);
- });
-
- it("clicking a card closes the fan", () => {
- const card = document.querySelector("#id_role_select .card");
- card.click();
- expect(document.getElementById("id_role_select")).toBeNull();
- });
-
- it("clicking a card appends a .card to #id_inv_role_card", () => {
- const card = document.querySelector("#id_role_select .card");
- card.click();
- expect(document.querySelector("#id_inv_role_card .card")).not.toBeNull();
- });
-
- it("clicking a card POSTs to the select_role URL", () => {
- const card = document.querySelector("#id_role_select .card");
- card.click();
- expect(window.fetch).toHaveBeenCalledWith(
- "/epic/room/test-uuid/select-role",
- jasmine.objectContaining({ method: "POST" })
- );
- });
-
- it("clicking a card results in exactly one card in inventory", () => {
- const card = document.querySelector("#id_role_select .card");
- card.click();
- expect(document.querySelectorAll("#id_inv_role_card .card").length).toBe(1);
- });
- });
-
- // ------------------------------------------------------------------ //
- // Backdrop click //
- // ------------------------------------------------------------------ //
-
- describe("backdrop click", () => {
- it("closes the fan", () => {
- RoleSelect.openFan();
- document.querySelector(".role-select-backdrop").click();
- expect(document.getElementById("id_role_select")).toBeNull();
- });
-
- it("does not add a card to inventory", () => {
- RoleSelect.openFan();
- document.querySelector(".role-select-backdrop").click();
- expect(document.querySelector("#id_inv_role_card .card")).toBeNull();
- });
- });
-
- // ------------------------------------------------------------------ //
- // room:roles_revealed event //
- // ------------------------------------------------------------------ //
-
- describe("room:roles_revealed event", () => {
- let reloadCalled;
-
- beforeEach(() => {
- reloadCalled = false;
- RoleSelect.setReload(() => { reloadCalled = true; });
- });
-
- afterEach(() => {
- RoleSelect.setReload(() => { window.location.reload(); });
- });
-
- it("triggers a page reload", () => {
- window.dispatchEvent(new CustomEvent("room:roles_revealed", { detail: {} }));
- expect(reloadCalled).toBe(true);
- });
- });
-
- // ------------------------------------------------------------------ //
- // room:turn_changed event //
- // ------------------------------------------------------------------ //
-
- describe("room:turn_changed event", () => {
- let stack;
-
- beforeEach(() => {
- // Six table seats, slot 1 starts active
- for (let i = 1; i <= 6; i++) {
- const seat = document.createElement("div");
- seat.className = "table-seat" + (i === 1 ? " active" : "");
- seat.dataset.slot = String(i);
- seat.innerHTML = '';
- testDiv.appendChild(seat);
- }
- stack = document.createElement("div");
- stack.className = "card-stack";
- stack.dataset.state = "ineligible";
- stack.dataset.userSlots = "1";
- stack.dataset.starterRoles = "";
- testDiv.appendChild(stack);
- });
-
- it("moves .active to the newly active seat", () => {
- window.dispatchEvent(new CustomEvent("room:turn_changed", {
- detail: { active_slot: 2 }
- }));
- expect(
- testDiv.querySelector(".table-seat.active").dataset.slot
- ).toBe("2");
- });
-
- it("removes .active from the previously active seat", () => {
- window.dispatchEvent(new CustomEvent("room:turn_changed", {
- detail: { active_slot: 2 }
- }));
- expect(
- testDiv.querySelector(".table-seat[data-slot='1']").classList.contains("active")
- ).toBe(false);
- });
-
- it("sets data-state to eligible when active_slot matches user slot", () => {
- window.dispatchEvent(new CustomEvent("room:turn_changed", {
- detail: { active_slot: 1 }
- }));
- expect(stack.dataset.state).toBe("eligible");
- });
-
- it("sets data-state to ineligible when active_slot does not match", () => {
- stack.dataset.state = "eligible";
- window.dispatchEvent(new CustomEvent("room:turn_changed", {
- detail: { active_slot: 2 }
- }));
- expect(stack.dataset.state).toBe("ineligible");
- });
-
- it("clicking stack opens fan when newly eligible", () => {
- window.dispatchEvent(new CustomEvent("room:turn_changed", {
- detail: { active_slot: 1 }
- }));
- stack.click();
- expect(document.querySelector(".role-select-backdrop")).not.toBeNull();
- });
-
- it("clicking stack does not open fan when ineligible", () => {
- // Make eligible first (adds listener), then flip back to ineligible
- window.dispatchEvent(new CustomEvent("room:turn_changed", {
- detail: { active_slot: 1 }
- }));
- window.dispatchEvent(new CustomEvent("room:turn_changed", {
- detail: { active_slot: 2 }
- }));
- stack.click();
- expect(document.querySelector(".role-select-backdrop")).toBeNull();
- });
- });
-
- // ------------------------------------------------------------------ //
- // click-guard integration //
- // ------------------------------------------------------------------ //
- // NOTE: cascade prevention (outside-click on backdrop not closing the //
- // fan while the guard is active) relies on the guard portal's capture- //
- // phase stopPropagation, which lives in base.html and requires //
- // integration testing. The callback contract is fully covered below. //
- // ------------------------------------------------------------------ //
-
- describe("click-guard integration", () => {
- let guardAnchor, guardMessage, guardConfirm, guardDismiss;
-
- beforeEach(() => {
- window.showGuard = jasmine.createSpy("showGuard").and.callFake(
- (anchor, message, onConfirm, onDismiss) => {
- guardAnchor = anchor;
- guardMessage = message;
- guardConfirm = onConfirm;
- guardDismiss = onDismiss;
- }
- );
- RoleSelect.openFan();
- });
-
- describe("clicking a card", () => {
- let card;
-
- beforeEach(() => {
- card = document.querySelector("#id_role_select .card");
- card.click();
- });
-
- it("calls window.showGuard", () => {
- expect(window.showGuard).toHaveBeenCalled();
- });
-
- it("passes the card element as the anchor", () => {
- expect(guardAnchor).toBe(card);
- });
-
- it("message contains the role name", () => {
- const roleName = card.querySelector(".card-role-name").textContent.trim();
- expect(guardMessage).toContain(roleName);
- });
-
- it("message contains the role code", () => {
- expect(guardMessage).toContain(card.dataset.role);
- });
-
- it("message contains a ", () => {
- expect(guardMessage).toContain(" ");
- });
-
- it("does not immediately close the fan", () => {
- expect(document.querySelector(".role-select-backdrop")).not.toBeNull();
- });
-
- it("does not immediately POST to the select_role URL", () => {
- expect(window.fetch).not.toHaveBeenCalled();
- });
-
- it("adds .flipped to the card", () => {
- expect(card.classList.contains("flipped")).toBe(true);
- });
-
- it("adds .guard-active to the card", () => {
- expect(card.classList.contains("guard-active")).toBe(true);
- });
-
- it("mouseleave does not remove .flipped while guard is active", () => {
- card.dispatchEvent(new MouseEvent("mouseleave"));
- expect(card.classList.contains("flipped")).toBe(true);
- });
- });
-
- describe("confirming the guard (OK)", () => {
- let card;
-
- beforeEach(() => {
- card = document.querySelector("#id_role_select .card");
- card.click();
- guardConfirm();
- });
-
- it("removes .guard-active from the card", () => {
- expect(card.classList.contains("guard-active")).toBe(false);
- });
-
- it("closes the fan", () => {
- expect(document.querySelector(".role-select-backdrop")).toBeNull();
- });
-
- it("POSTs to the select_role URL", () => {
- expect(window.fetch).toHaveBeenCalledWith(
- "/epic/room/test-uuid/select-role",
- jasmine.objectContaining({ method: "POST" })
- );
- });
-
- it("appends a .card to #id_inv_role_card", () => {
- expect(document.querySelector("#id_inv_role_card .card")).not.toBeNull();
- });
- });
-
- describe("dismissing the guard (NVM or outside click)", () => {
- let card;
-
- beforeEach(() => {
- card = document.querySelector("#id_role_select .card");
- card.click();
- guardDismiss();
- });
-
- it("removes .guard-active from the card", () => {
- expect(card.classList.contains("guard-active")).toBe(false);
- });
-
- it("removes .flipped from the card", () => {
- expect(card.classList.contains("flipped")).toBe(false);
- });
-
- it("leaves the fan open", () => {
- expect(document.querySelector(".role-select-backdrop")).not.toBeNull();
- });
-
- it("does not POST to the select_role URL", () => {
- expect(window.fetch).not.toHaveBeenCalled();
- });
-
- it("does not add a card to inventory", () => {
- expect(document.querySelector("#id_inv_role_card .card")).toBeNull();
- });
-
- it("restores normal mouseleave behaviour on the card", () => {
- card.dispatchEvent(new MouseEvent("mouseenter"));
- card.dispatchEvent(new MouseEvent("mouseleave"));
- expect(card.classList.contains("flipped")).toBe(false);
- });
- });
- });
-});
diff --git a/src/static/tests/Spec.js b/src/static/tests/Spec.js
deleted file mode 100644
index 63624c6..0000000
--- a/src/static/tests/Spec.js
+++ /dev/null
@@ -1,57 +0,0 @@
-console.log("Spec.js is loading");
-
-describe("GameArray JavaScript", () => {
- const inputId= "id_text";
- const errorClass = "invalid-feedback";
- const inputSelector = `#${inputId}`;
- const errorSelector = `.${errorClass}`;
- let testDiv;
- let textInput;
- let errorMsg;
-
- beforeEach(() => {
- console.log("beforeEach");
- testDiv = document.createElement("div");
- testDiv.innerHTML = `
-
- `;
- document.body.appendChild(testDiv);
- textInput = document.querySelector(inputSelector);
- errorMsg = document.querySelector(errorSelector);
- });
-
- afterEach(() => {
- testDiv.remove();
- });
-
- it("should have a useful html fixture", () => {
- console.log("in test 1");
- expect(errorMsg.checkVisibility()).toBe(true);
- });
-
- it("should hide error message on input", () => {
- console.log("in test 2");
- initialize(inputSelector);
- textInput.dispatchEvent(new InputEvent("input"));
-
- expect(errorMsg.checkVisibility()).toBe(false);
- });
-
- it("should not hide error message before event is fired", () => {
- console.log("in test 3");
- initialize(inputSelector);
-
- expect(errorMsg.checkVisibility()).toBe(true);
- });
-});
\ No newline at end of file
diff --git a/src/static/tests/SpecRunner.html b/src/static/tests/SpecRunner.html
deleted file mode 100644
index e6fe12b..0000000
--- a/src/static/tests/SpecRunner.html
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- Jasmine Spec Runner
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/static/tests/TraySpec.js b/src/static/tests/TraySpec.js
deleted file mode 100644
index 264275f..0000000
--- a/src/static/tests/TraySpec.js
+++ /dev/null
@@ -1,425 +0,0 @@
-// ── TraySpec.js ───────────────────────────────────────────────────────────────
-//
-// Unit specs for tray.js — the per-seat, per-room slide-out panel anchored
-// to the right edge of the viewport.
-//
-// DOM contract assumed by the module:
-// #id_tray_wrap — outermost container; JS sets style.left for positioning
-// #id_tray_btn — the drawer-handle button
-// #id_tray — the tray panel (hidden by default)
-//
-// Public API under test:
-// Tray.init() — compute bounds, apply vertical bounds, attach listeners
-// Tray.open() — reveal tray, animate wrap to minLeft
-// Tray.close() — hide tray, animate wrap to maxLeft
-// Tray.isOpen() — state predicate
-// Tray.reset() — restore initial state (for afterEach)
-//
-// Drag model: tray follows pointer in real-time; position persists on release.
-// Any leftward drag opens the tray.
-// Drag > 10px suppresses the subsequent click event.
-//
-// ─────────────────────────────────────────────────────────────────────────────
-
-describe("Tray", () => {
- let btn, tray, wrap;
-
- beforeEach(() => {
- wrap = document.createElement("div");
- wrap.id = "id_tray_wrap";
-
- btn = document.createElement("button");
- btn.id = "id_tray_btn";
-
- tray = document.createElement("div");
- tray.id = "id_tray";
- tray.style.display = "none";
-
- wrap.appendChild(btn);
- document.body.appendChild(wrap);
- document.body.appendChild(tray);
-
- Tray._testSetLandscape(false); // force portrait regardless of window size
- Tray.init();
- });
-
- afterEach(() => {
- Tray.reset();
- wrap.remove();
- tray.remove();
- });
-
- // ---------------------------------------------------------------------- //
- // open() //
- // ---------------------------------------------------------------------- //
-
- describe("open()", () => {
- it("makes #id_tray visible", () => {
- Tray.open();
- expect(tray.style.display).not.toBe("none");
- });
-
- it("adds .open to #id_tray_btn", () => {
- Tray.open();
- expect(btn.classList.contains("open")).toBe(true);
- });
-
- it("sets wrap left to minLeft (0)", () => {
- Tray.open();
- expect(wrap.style.left).toBe("0px");
- });
-
- it("calling open() twice does not duplicate .open", () => {
- Tray.open();
- Tray.open();
- const openCount = btn.className.split(" ").filter(c => c === "open").length;
- expect(openCount).toBe(1);
- });
- });
-
- // ---------------------------------------------------------------------- //
- // close() //
- // ---------------------------------------------------------------------- //
-
- describe("close()", () => {
- beforeEach(() => Tray.open());
-
- it("hides #id_tray after slide + snap both complete", () => {
- Tray.close();
- wrap.dispatchEvent(new TransitionEvent("transitionend", { propertyName: "left" }));
- wrap.dispatchEvent(new Event("animationend"));
- expect(tray.style.display).toBe("none");
- });
-
- it("adds .snap to wrap after slide transition completes", () => {
- Tray.close();
- wrap.dispatchEvent(new TransitionEvent("transitionend", { propertyName: "left" }));
- expect(wrap.classList.contains("snap")).toBe(true);
- });
-
- it("removes .snap from wrap once animationend fires", () => {
- Tray.close();
- wrap.dispatchEvent(new TransitionEvent("transitionend", { propertyName: "left" }));
- wrap.dispatchEvent(new Event("animationend"));
- expect(wrap.classList.contains("snap")).toBe(false);
- });
-
- it("removes .open from #id_tray_btn", () => {
- Tray.close();
- expect(btn.classList.contains("open")).toBe(false);
- });
-
- it("sets wrap left to maxLeft", () => {
- Tray.close();
- expect(parseInt(wrap.style.left, 10)).toBeGreaterThan(0);
- });
-
- it("does not throw if already closed", () => {
- Tray.close();
- expect(() => Tray.close()).not.toThrow();
- });
- });
-
- // ---------------------------------------------------------------------- //
- // isOpen() //
- // ---------------------------------------------------------------------- //
-
- describe("isOpen()", () => {
- it("returns false by default", () => {
- expect(Tray.isOpen()).toBe(false);
- });
-
- it("returns true after open()", () => {
- Tray.open();
- expect(Tray.isOpen()).toBe(true);
- });
-
- it("returns false after close()", () => {
- Tray.open();
- Tray.close();
- expect(Tray.isOpen()).toBe(false);
- });
- });
-
- // ---------------------------------------------------------------------- //
- // Click when closed — wobble wrap, do not open //
- // ---------------------------------------------------------------------- //
-
- describe("clicking btn when closed", () => {
- it("adds .wobble to wrap", () => {
- btn.click();
- expect(wrap.classList.contains("wobble")).toBe(true);
- });
-
- it("does not open the tray", () => {
- btn.click();
- expect(Tray.isOpen()).toBe(false);
- });
-
- it("removes .wobble once animationend fires on wrap", () => {
- btn.click();
- wrap.dispatchEvent(new Event("animationend"));
- expect(wrap.classList.contains("wobble")).toBe(false);
- });
- });
-
- // ---------------------------------------------------------------------- //
- // Click when open — close, no wobble //
- // ---------------------------------------------------------------------- //
-
- describe("clicking btn when open", () => {
- beforeEach(() => Tray.open());
-
- it("closes the tray", () => {
- btn.click();
- expect(Tray.isOpen()).toBe(false);
- });
-
- it("does not add .wobble", () => {
- btn.click();
- expect(wrap.classList.contains("wobble")).toBe(false);
- });
- });
-
- // ---------------------------------------------------------------------- //
- // Drag interaction — continuous positioning //
- // ---------------------------------------------------------------------- //
-
- describe("drag interaction", () => {
- function simulateDrag(deltaX) {
- const startX = 800;
- btn.dispatchEvent(new PointerEvent("pointerdown", { clientX: startX, bubbles: true }));
- btn.dispatchEvent(new PointerEvent("pointermove", { clientX: startX + deltaX, bubbles: true }));
- btn.dispatchEvent(new PointerEvent("pointerup", { clientX: startX + deltaX, bubbles: true }));
- }
-
- it("dragging left opens the tray", () => {
- simulateDrag(-60);
- expect(Tray.isOpen()).toBe(true);
- });
-
- it("any leftward drag opens the tray", () => {
- simulateDrag(-20);
- expect(Tray.isOpen()).toBe(true);
- });
-
- it("dragging right does not open the tray", () => {
- simulateDrag(100);
- expect(Tray.isOpen()).toBe(false);
- });
-
- it("drag > 10px suppresses the subsequent click", () => {
- simulateDrag(-60);
- btn.click(); // should be swallowed — tray stays open
- expect(Tray.isOpen()).toBe(true);
- });
-
- it("does not add .wobble during drag", () => {
- simulateDrag(-60);
- expect(wrap.classList.contains("wobble")).toBe(false);
- });
- });
-
- // ---------------------------------------------------------------------- //
- // Landscape mode — Y-axis drag, top-positioned wrap //
- // ---------------------------------------------------------------------- //
-
- describe("landscape mode", () => {
- // Re-init in landscape after the portrait init from outer beforeEach.
- beforeEach(() => {
- Tray.reset();
- Tray._testSetLandscape(true);
- Tray.init();
- });
-
- function simulateDragY(deltaY) {
- const startY = 50;
- btn.dispatchEvent(new PointerEvent("pointerdown", { clientY: startY, clientX: 0, bubbles: true }));
- btn.dispatchEvent(new PointerEvent("pointermove", { clientY: startY + deltaY, clientX: 0, bubbles: true }));
- btn.dispatchEvent(new PointerEvent("pointerup", { clientY: startY + deltaY, clientX: 0, bubbles: true }));
- }
-
- // ── open() in landscape ─────────────────────────────────────────── //
-
- describe("open()", () => {
- it("makes #id_tray visible", () => {
- Tray.open();
- expect(tray.style.display).not.toBe("none");
- });
-
- it("adds .open to #id_tray_btn", () => {
- Tray.open();
- expect(btn.classList.contains("open")).toBe(true);
- });
-
- it("positions wrap via style.top, not style.left", () => {
- Tray.open();
- expect(wrap.style.top).not.toBe("");
- expect(wrap.style.left).toBe("");
- });
- });
-
- // ── close() in landscape ────────────────────────────────────────── //
-
- describe("close()", () => {
- beforeEach(() => Tray.open());
-
- it("closes the tray (display not toggled in landscape)", () => {
- Tray.close();
- expect(Tray.isOpen()).toBe(false);
- });
-
- it("removes .open from #id_tray_btn", () => {
- Tray.close();
- expect(btn.classList.contains("open")).toBe(false);
- });
-
- it("closed top is less than open top (wrap slides up to close)", () => {
- const openTop = parseInt(wrap.style.top, 10);
- Tray.close();
- const closedTop = parseInt(wrap.style.top, 10);
- expect(closedTop).toBeLessThan(openTop);
- });
-
- it("adds .snap to wrap after top transition completes", () => {
- Tray.close();
- wrap.dispatchEvent(new TransitionEvent("transitionend", { propertyName: "top" }));
- expect(wrap.classList.contains("snap")).toBe(true);
- });
-
- it("removes .snap from wrap once animationend fires", () => {
- Tray.close();
- wrap.dispatchEvent(new TransitionEvent("transitionend", { propertyName: "top" }));
- wrap.dispatchEvent(new Event("animationend"));
- expect(wrap.classList.contains("snap")).toBe(false);
- });
- });
-
- // ── drag — Y axis ──────────────────────────────────────────────── //
-
- describe("drag interaction", () => {
- it("dragging down opens the tray", () => {
- simulateDragY(100);
- expect(Tray.isOpen()).toBe(true);
- });
-
- it("dragging up does not open the tray", () => {
- simulateDragY(-100);
- expect(Tray.isOpen()).toBe(false);
- });
-
- it("drag > 10px downward suppresses subsequent click", () => {
- simulateDragY(100);
- btn.click(); // should be swallowed — tray stays open
- expect(Tray.isOpen()).toBe(true);
- });
-
- it("does not set style.left (Y axis only)", () => {
- simulateDragY(100);
- expect(wrap.style.left).toBe("");
- });
-
- it("does not add .wobble during drag", () => {
- simulateDragY(100);
- expect(wrap.classList.contains("wobble")).toBe(false);
- });
- });
-
- // ── click when closed — wobble, no open ───────────────────────── //
-
- describe("clicking btn when closed", () => {
- it("adds .wobble to wrap", () => {
- btn.click();
- expect(wrap.classList.contains("wobble")).toBe(true);
- });
-
- it("does not open the tray", () => {
- btn.click();
- expect(Tray.isOpen()).toBe(false);
- });
- });
-
- // ── click when open — close ────────────────────────────────────── //
-
- describe("clicking btn when open", () => {
- beforeEach(() => Tray.open());
-
- it("closes the tray", () => {
- btn.click();
- expect(Tray.isOpen()).toBe(false);
- });
- });
-
- // ── init positions wrap at closed (top) ────────────────────────── //
-
- it("init sets wrap to closed position (top < 0 or = maxTop)", () => {
- // After landscape init with no real elements, _maxTop = -(wrapH_fallback - handleH_fallback)
- // which will be negative. Wrap starts off-screen above.
- const top = parseInt(wrap.style.top, 10);
- expect(top).toBeLessThan(0);
- });
-
- // ── resize closes landscape tray ─────────────────────────────── //
-
- describe("resize closes the tray", () => {
- it("closes when landscape tray is open", () => {
- Tray.open();
- window.dispatchEvent(new Event("resize"));
- expect(Tray.isOpen()).toBe(false);
- });
-
- it("removes .open from btn on resize", () => {
- Tray.open();
- window.dispatchEvent(new Event("resize"));
- expect(btn.classList.contains("open")).toBe(false);
- });
-
- it("resets wrap to closed top position on resize", () => {
- Tray.open();
- window.dispatchEvent(new Event("resize"));
- expect(parseInt(wrap.style.top, 10)).toBeLessThan(0);
- });
-
- it("does not re-open a closed tray on resize", () => {
- window.dispatchEvent(new Event("resize"));
- expect(Tray.isOpen()).toBe(false);
- });
- });
- });
-
- // ---------------------------------------------------------------------- //
- // window resize — portrait //
- // ---------------------------------------------------------------------- //
-
- describe("window resize (portrait)", () => {
- it("closes the tray when open", () => {
- Tray.open();
- window.dispatchEvent(new Event("resize"));
- expect(Tray.isOpen()).toBe(false);
- });
-
- it("removes .open from btn on resize", () => {
- Tray.open();
- window.dispatchEvent(new Event("resize"));
- expect(btn.classList.contains("open")).toBe(false);
- });
-
- it("hides the tray panel on resize", () => {
- Tray.open();
- window.dispatchEvent(new Event("resize"));
- expect(tray.style.display).toBe("none");
- });
-
- it("resets wrap to closed left position on resize", () => {
- Tray.open();
- expect(wrap.style.left).toBe("0px");
- window.dispatchEvent(new Event("resize"));
- expect(parseInt(wrap.style.left, 10)).toBeGreaterThan(0);
- });
-
- it("does not re-open a closed tray on resize", () => {
- window.dispatchEvent(new Event("resize"));
- expect(Tray.isOpen()).toBe(false);
- });
- });
-});
diff --git a/src/static/tests/lib/jasmine-6.0.1/boot0.js b/src/static/tests/lib/jasmine-6.0.1/boot0.js
deleted file mode 100644
index 5a97826..0000000
--- a/src/static/tests/lib/jasmine-6.0.1/boot0.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-Copyright (c) 2008-2019 Pivotal Labs
-Copyright (c) 2008-2026 The Jasmine developers
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-'use strict';
-
-/**
- This file starts the process of "booting" Jasmine. It initializes Jasmine,
- makes its globals available, and creates the env. This file should be loaded
- after `jasmine.js` and `jasmine_html.js`, but before `boot1.js` or any project
- source files or spec files are loaded.
- */
-(function() {
- const jasmineRequire = window.jasmineRequire || require('./jasmine.js');
-
- /**
- * ## Require & Instantiate
- *
- * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
- */
- const jasmine = jasmineRequire.core(jasmineRequire),
- global = jasmine.getGlobal();
- global.jasmine = jasmine;
-
- /**
- * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
- */
- jasmineRequire.html(jasmine);
-
- /**
- * Create the Jasmine environment. This is used to run all specs in a project.
- */
- const env = jasmine.getEnv();
-
- /**
- * ## The Global Interface
- *
- * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
- */
- const jasmineInterface = jasmineRequire.interface(jasmine, env);
-
- /**
- * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
- */
- for (const property in jasmineInterface) {
- global[property] = jasmineInterface[property];
- }
-})();
diff --git a/src/static/tests/lib/jasmine-6.0.1/boot1.js b/src/static/tests/lib/jasmine-6.0.1/boot1.js
deleted file mode 100644
index ebcb5bb..0000000
--- a/src/static/tests/lib/jasmine-6.0.1/boot1.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-Copyright (c) 2008-2019 Pivotal Labs
-Copyright (c) 2008-2026 The Jasmine developers
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-'use strict';
-
-/**
- This file finishes 'booting' Jasmine, performing all of the necessary
- initialization before executing the loaded environment and all of a project's
- specs. This file should be loaded after `boot0.js` but before any project
- source files or spec files are loaded. Thus this file can also be used to
- customize Jasmine for a project.
-
- If a project is using Jasmine via the standalone distribution, this file can
- be customized directly. If you only wish to configure the Jasmine env, you
- can load another file that calls `jasmine.getEnv().configure({...})`
- after `boot0.js` is loaded and before this file is loaded.
- */
-
-(function() {
- const env = jasmine.getEnv();
- const urls = new jasmine.HtmlReporterV2Urls();
-
- /**
- * Configures Jasmine based on the current set of query parameters. This
- * supports all parameters set by the HTML reporter as well as
- * spec=partialPath, which filters out specs whose paths don't contain the
- * parameter.
- */
- env.configure(urls.configFromCurrentUrl());
-
- const currentWindowOnload = window.onload;
- window.onload = function() {
- if (currentWindowOnload) {
- currentWindowOnload();
- }
-
- // The HTML reporter needs to be set up here so it can access the DOM. Other
- // reporters can be added at any time before env.execute() is called.
- const htmlReporter = new jasmine.HtmlReporterV2({ env, urls });
- env.addReporter(htmlReporter);
- env.execute();
- };
-})();
diff --git a/src/static/tests/lib/jasmine-6.0.1/jasmine-html.js b/src/static/tests/lib/jasmine-6.0.1/jasmine-html.js
deleted file mode 100644
index c3c92d5..0000000
--- a/src/static/tests/lib/jasmine-6.0.1/jasmine-html.js
+++ /dev/null
@@ -1,1863 +0,0 @@
-/*
-Copyright (c) 2008-2019 Pivotal Labs
-Copyright (c) 2008-2026 The Jasmine developers
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-// eslint-disable-next-line no-var
-var jasmineRequire = window.jasmineRequire || require('./jasmine.js');
-
-jasmineRequire.html = function(j$) {
- j$.private.ResultsNode = jasmineRequire.ResultsNode();
- j$.private.ResultsStateBuilder = jasmineRequire.ResultsStateBuilder(j$);
- j$.private.htmlReporterUtils = jasmineRequire.htmlReporterUtils(j$);
- j$.private.AlertsView = jasmineRequire.AlertsView(j$);
- j$.private.OverallStatusBar = jasmineRequire.OverallStatusBar(j$);
- j$.private.Banner = jasmineRequire.Banner(j$);
- j$.private.SymbolsView = jasmineRequire.SymbolsView(j$);
- j$.private.SummaryTreeView = jasmineRequire.SummaryTreeView(j$);
- j$.private.FailuresView = jasmineRequire.FailuresView(j$);
- j$.private.PerformanceView = jasmineRequire.PerformanceView(j$);
- j$.private.TabBar = jasmineRequire.TabBar(j$);
- j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
- j$.HtmlReporterV2Urls = jasmineRequire.HtmlReporterV2Urls(j$);
- j$.HtmlReporterV2 = jasmineRequire.HtmlReporterV2(j$);
- j$.QueryString = jasmineRequire.QueryString();
- j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(j$);
- j$.private.HtmlSpecFilterV2 = jasmineRequire.HtmlSpecFilterV2();
-};
-
-jasmineRequire.HtmlReporter = function(j$) {
- 'use strict';
-
- const { createDom, noExpectations } = j$.private.htmlReporterUtils;
-
- /**
- * @class HtmlReporter
- * @classdesc Displays results and allows re-running individual specs and suites.
- * @implements {Reporter}
- * @param options Options object. See lib/jasmine-core/boot1.js for details.
- * @since 1.2.0
- * @deprecated Use {@link HtmlReporterV2} instead.
- */
- class HtmlReporter {
- #env;
- #getContainer;
- #navigateWithNewParam;
- #urlBuilder;
- #filterSpecs;
- #stateBuilder;
- #config;
- #htmlReporterMain;
-
- // Sub-views
- #alerts;
- #symbols;
- #banner;
- #failures;
-
- constructor(options) {
- this.#env = options.env;
-
- this.#getContainer = options.getContainer;
- this.#navigateWithNewParam =
- options.navigateWithNewParam || function() {};
- this.#urlBuilder = new UrlBuilder(
- options.addToExistingQueryString || defaultQueryString
- );
- this.#filterSpecs = options.filterSpecs;
- }
-
- /**
- * Initializes the reporter. Should be called before {@link Env#execute}.
- * @function
- * @name HtmlReporter#initialize
- */
- initialize() {
- this.#env.deprecated(
- 'HtmlReporter and HtmlSpecFilter are deprecated. Use HtmlReporterV2 instead.'
- );
- this.#clearPrior();
- this.#config = this.#env ? this.#env.configuration() : {};
-
- this.#stateBuilder = new j$.private.ResultsStateBuilder();
-
- this.#alerts = new j$.private.AlertsView(this.#urlBuilder);
- this.#symbols = new j$.private.SymbolsView();
- this.#banner = new j$.private.Banner(this.#navigateWithNewParam);
- this.#failures = new j$.private.FailuresView(this.#urlBuilder);
- this.#htmlReporterMain = createDom(
- 'div',
- { className: 'jasmine_html-reporter' },
- this.#banner.rootEl,
- this.#symbols.rootEl,
- this.#alerts.rootEl,
- this.#failures.rootEl
- );
- this.#getContainer().appendChild(this.#htmlReporterMain);
- }
-
- jasmineStarted(options) {
- this.#stateBuilder.jasmineStarted(options);
- }
-
- suiteStarted(result) {
- this.#stateBuilder.suiteStarted(result);
- }
-
- suiteDone(result) {
- this.#stateBuilder.suiteDone(result);
-
- if (result.status === 'failed') {
- this.#failures.append(result, this.#stateBuilder.currentParent);
- }
- }
-
- specStarted() {}
-
- specDone(result) {
- this.#stateBuilder.specDone(result);
- this.#symbols.append(result, this.#config);
-
- if (noExpectations(result)) {
- const noSpecMsg = "Spec '" + result.fullName + "' has no expectations.";
- if (result.status === 'failed') {
- // eslint-disable-next-line no-console
- console.error(noSpecMsg);
- } else {
- // eslint-disable-next-line no-console
- console.warn(noSpecMsg);
- }
- }
-
- if (result.status === 'failed') {
- this.#failures.append(result, this.#stateBuilder.currentParent);
- }
- }
-
- jasmineDone(doneResult) {
- this.#stateBuilder.jasmineDone(doneResult);
- this.#banner.showOptionsMenu(this.#config);
-
- if (
- this.#stateBuilder.specsExecuted < this.#stateBuilder.totalSpecsDefined
- ) {
- this.#alerts.addSkipped(
- this.#stateBuilder.specsExecuted,
- this.#stateBuilder.totalSpecsDefined
- );
- }
-
- const statusBar = new j$.private.OverallStatusBar(this.#urlBuilder);
- statusBar.showDone(doneResult, this.#stateBuilder);
- this.#alerts.addBar(statusBar.rootEl);
-
- if (doneResult.failedExpectations) {
- for (const f of doneResult.failedExpectations) {
- this.#alerts.addGlobalFailure(f);
- }
- }
-
- for (const dw of this.#stateBuilder.deprecationWarnings) {
- this.#alerts.addDeprecationWarning(dw);
- }
-
- const results = this.#find('.jasmine-results');
- const summary = new j$.private.SummaryTreeView(
- this.#urlBuilder,
- this.#filterSpecs
- );
- summary.addResults(this.#stateBuilder.topResults);
- results.appendChild(summary.rootEl);
-
- if (this.#stateBuilder.anyNonTopSuiteFailures) {
- this.#addFailureToggle();
- this.#setMenuModeTo('jasmine-failure-list');
- this.#failures.show();
- }
- }
-
- #addFailureToggle() {
- const onClickFailures = () => this.#setMenuModeTo('jasmine-failure-list');
- const onClickSpecList = () => this.#setMenuModeTo('jasmine-spec-list');
- const failuresLink = createDom(
- 'a',
- { className: 'jasmine-failures-menu', href: '#' },
- 'Failures'
- );
- let specListLink = createDom(
- 'a',
- { className: 'jasmine-spec-list-menu', href: '#' },
- 'Spec List'
- );
-
- failuresLink.onclick = function() {
- onClickFailures();
- return false;
- };
-
- specListLink.onclick = function() {
- onClickSpecList();
- return false;
- };
-
- this.#alerts.addBar(
- createDom(
- 'span',
- { className: 'jasmine-menu jasmine-bar jasmine-spec-list' },
- [createDom('span', {}, 'Spec List | '), failuresLink]
- )
- );
- this.#alerts.addBar(
- createDom(
- 'span',
- { className: 'jasmine-menu jasmine-bar jasmine-failure-list' },
- [specListLink, createDom('span', {}, ' | Failures ')]
- )
- );
- }
-
- #find(selector) {
- return this.#getContainer().querySelector(
- '.jasmine_html-reporter ' + selector
- );
- }
-
- #clearPrior() {
- const oldReporter = this.#find('');
-
- if (oldReporter) {
- this.#getContainer().removeChild(oldReporter);
- }
- }
-
- #setMenuModeTo(mode) {
- this.#htmlReporterMain.setAttribute(
- 'class',
- 'jasmine_html-reporter ' + mode
- );
- }
- }
-
- class UrlBuilder {
- #addToExistingQueryString;
-
- constructor(addToExistingQueryString) {
- this.#addToExistingQueryString = function(k, v) {
- // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
- return (
- (window.location.pathname || '') + addToExistingQueryString(k, v)
- );
- };
- }
-
- suiteHref(suite) {
- const els = [];
-
- while (suite && suite.parent) {
- els.unshift(suite.result.description);
- suite = suite.parent;
- }
-
- return this.#addToExistingQueryString('spec', els.join(' '));
- }
-
- specHref(result) {
- return this.#addToExistingQueryString('spec', result.fullName);
- }
-
- runAllHref() {
- return this.#addToExistingQueryString('spec', '');
- }
-
- seedHref(seed) {
- return this.#addToExistingQueryString('seed', seed);
- }
- }
-
- function defaultQueryString(key, value) {
- return '?' + key + '=' + value;
- }
-
- return HtmlReporter;
-};
-
-jasmineRequire.HtmlSpecFilter = function(j$) {
- 'use strict';
-
- /**
- * @class HtmlSpecFilter
- * @param options Options object. See lib/jasmine-core/boot1.js for details.
- * @deprecated Use {@link HtmlReporterV2Urls} instead.
- */
- function HtmlSpecFilter(options) {
- const env = options?.env ?? j$.getEnv();
- env.deprecated(
- 'HtmlReporter and HtmlSpecFilter are deprecated. Use HtmlReporterV2 instead.'
- );
-
- const filterString =
- options &&
- options.filterString &&
- options.filterString() &&
- options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
- const filterPattern = new RegExp(filterString);
-
- /**
- * Determines whether the spec with the specified name should be executed.
- * @name HtmlSpecFilter#matches
- * @function
- * @param {string} specName The full name of the spec
- * @returns {boolean}
- */
- this.matches = function(specName) {
- return filterPattern.test(specName);
- };
- }
-
- return HtmlSpecFilter;
-};
-
-jasmineRequire.ResultsNode = function() {
- 'use strict';
-
- function ResultsNode(result, type, parent) {
- this.result = result;
- this.type = type;
- this.parent = parent;
-
- this.children = [];
-
- this.addChild = function(result, type) {
- this.children.push(new ResultsNode(result, type, this));
- };
-
- this.last = function() {
- return this.children[this.children.length - 1];
- };
-
- this.updateResult = function(result) {
- this.result = result;
- };
- }
-
- return ResultsNode;
-};
-
-jasmineRequire.QueryString = function() {
- 'use strict';
-
- /**
- * Reads and manipulates the query string.
- * @since 2.0.0
- */
- class QueryString {
- #getWindowLocation;
-
- /**
- * @param options Object with a getWindowLocation property, which should be
- * a function returning the current value of window.location.
- */
- constructor(options) {
- this.#getWindowLocation = options.getWindowLocation;
- }
-
- /**
- * Sets the specified query parameter and navigates to the resulting URL.
- * @param {string} key
- * @param {string} value
- */
- navigateWithNewParam(key, value) {
- this.#getWindowLocation().search = this.fullStringWithNewParam(
- key,
- value
- );
- }
-
- /**
- * Returns a new URL based on the current location, with the specified
- * query parameter set.
- * @param {string} key
- * @param {string} value
- * @return {string}
- */
- fullStringWithNewParam(key, value) {
- const paramMap = this.#queryStringToParamMap();
- paramMap[key] = value;
- return toQueryString(paramMap);
- }
-
- /**
- * Gets the value of the specified query parameter.
- * @param {string} key
- * @return {string}
- */
- getParam(key) {
- return this.#queryStringToParamMap()[key];
- }
-
- #queryStringToParamMap() {
- const paramStr = this.#getWindowLocation().search.substring(1);
- let params = [];
- const paramMap = {};
-
- if (paramStr.length > 0) {
- params = paramStr.split('&');
- for (let i = 0; i < params.length; i++) {
- const p = params[i].split('=');
- let value = decodeURIComponent(p[1]);
- if (value === 'true' || value === 'false') {
- value = JSON.parse(value);
- }
- paramMap[decodeURIComponent(p[0])] = value;
- }
- }
-
- return paramMap;
- }
- }
-
- function toQueryString(paramMap) {
- const qStrPairs = [];
- for (const prop in paramMap) {
- qStrPairs.push(
- encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])
- );
- }
- return '?' + qStrPairs.join('&');
- }
-
- return QueryString;
-};
-
-jasmineRequire.AlertsView = function(j$) {
- 'use strict';
-
- const { createDom } = j$.private.htmlReporterUtils;
- const errorBarClassName = 'jasmine-bar jasmine-errored';
- const afterAllMessagePrefix = 'AfterAll ';
-
- class AlertsView {
- #urlBuilder;
-
- constructor(urlBuilder) {
- this.#urlBuilder = urlBuilder;
- this.rootEl = createDom('div', { className: 'jasmine-alert' });
- }
-
- addSkipped(numExecuted, numDefined) {
- this.#createAndAdd(
- 'jasmine-bar jasmine-skipped',
- createDom(
- 'a',
- { href: this.#urlBuilder.runAllHref(), title: 'Run all specs' },
- `Ran ${numExecuted} of ${numDefined} specs - run all`
- )
- );
- }
-
- addGlobalFailure(failure) {
- this.#createAndAdd(
- errorBarClassName,
- this.#globalFailureMessage(failure)
- );
- }
-
- #globalFailureMessage(failure) {
- if (failure.globalErrorType === 'load') {
- const prefix = 'Error during loading: ' + failure.message;
-
- if (failure.filename) {
- return prefix + ' in ' + failure.filename + ' line ' + failure.lineno;
- } else {
- return prefix;
- }
- } else if (failure.globalErrorType === 'afterAll') {
- return afterAllMessagePrefix + failure.message;
- } else {
- return failure.message;
- }
- }
-
- addDeprecationWarning(dw) {
- const children = [];
- let context;
-
- switch (dw.runnableType) {
- case 'spec':
- context = '(in spec: ' + dw.runnableName + ')';
- break;
- case 'suite':
- context = '(in suite: ' + dw.runnableName + ')';
- break;
- default:
- context = '';
- }
-
- for (const line of dw.message.split('\n')) {
- children.push(line);
- children.push(createDom('br'));
- }
-
- children[0] = 'DEPRECATION: ' + children[0];
- children.push(context);
-
- if (dw.stack) {
- children.push(this.#createExpander(dw.stack));
- }
-
- this.#createAndAdd('jasmine-bar jasmine-warning', children);
- }
-
- addBar(el) {
- this.rootEl.appendChild(el);
- }
-
- #createAndAdd(className, children) {
- this.rootEl.appendChild(createDom('span', { className }, children));
- }
-
- #createExpander(stackTrace) {
- const expandLink = createDom('a', { href: '#' }, 'Show stack trace');
- const root = createDom(
- 'div',
- { className: 'jasmine-expander' },
- expandLink,
- createDom(
- 'div',
- { className: 'jasmine-expander-contents jasmine-stack-trace' },
- stackTrace
- )
- );
-
- expandLink.addEventListener('click', function(e) {
- e.preventDefault();
-
- if (root.classList.contains('jasmine-expanded')) {
- root.classList.remove('jasmine-expanded');
- expandLink.textContent = 'Show stack trace';
- } else {
- root.classList.add('jasmine-expanded');
- expandLink.textContent = 'Hide stack trace';
- }
- });
-
- return root;
- }
- }
-
- return AlertsView;
-};
-
-jasmineRequire.Banner = function(j$) {
- 'use strict';
-
- const { createDom } = j$.private.htmlReporterUtils;
-
- class Banner {
- #navigateWithNewParam;
- #omitHideDisabled;
-
- constructor(navigateWithNewParam, omitHideDisabled) {
- this.#navigateWithNewParam = navigateWithNewParam;
- this.#omitHideDisabled = omitHideDisabled;
- this.rootEl = createDom(
- 'div',
- { className: 'jasmine-banner' },
- createDom('a', {
- className: 'jasmine-title',
- href: 'http://jasmine.github.io/',
- target: '_blank'
- }),
- createDom('span', { className: 'jasmine-version' }, j$.version)
- );
- }
-
- showOptionsMenu(config) {
- this.rootEl.appendChild(this.#optionsMenu(config));
- }
-
- #optionsMenu(config) {
- const items = [
- createDom(
- 'div',
- { className: 'jasmine-stop-on-failure' },
- createDom('input', {
- className: 'jasmine-fail-fast',
- id: 'jasmine-fail-fast',
- type: 'checkbox'
- }),
- createDom(
- 'label',
- { className: 'jasmine-label', for: 'jasmine-fail-fast' },
- 'stop execution on spec failure'
- )
- ),
- createDom(
- 'div',
- { className: 'jasmine-throw-failures' },
- createDom('input', {
- className: 'jasmine-throw',
- id: 'jasmine-throw-failures',
- type: 'checkbox'
- }),
- createDom(
- 'label',
- { className: 'jasmine-label', for: 'jasmine-throw-failures' },
- 'stop spec on expectation failure'
- )
- ),
- createDom(
- 'div',
- { className: 'jasmine-random-order' },
- createDom('input', {
- className: 'jasmine-random',
- id: 'jasmine-random-order',
- type: 'checkbox'
- }),
- createDom(
- 'label',
- { className: 'jasmine-label', for: 'jasmine-random-order' },
- 'run tests in random order'
- )
- )
- ];
-
- if (!this.#omitHideDisabled) {
- items.push(
- createDom(
- 'div',
- { className: 'jasmine-hide-disabled' },
- createDom('input', {
- className: 'jasmine-disabled',
- id: 'jasmine-hide-disabled',
- type: 'checkbox'
- }),
- createDom(
- 'label',
- { className: 'jasmine-label', for: 'jasmine-hide-disabled' },
- 'hide disabled tests'
- )
- )
- );
- }
-
- const optionsMenuDom = createDom(
- 'div',
- { className: 'jasmine-run-options' },
- createDom('span', { className: 'jasmine-trigger' }, 'Options'),
- createDom('div', { className: 'jasmine-payload' }, items)
- );
-
- const failFastCheckbox = optionsMenuDom.querySelector(
- '#jasmine-fail-fast'
- );
- failFastCheckbox.checked = config.stopOnSpecFailure;
- failFastCheckbox.onclick = () => {
- this.#navigateWithNewParam(
- 'stopOnSpecFailure',
- !config.stopOnSpecFailure
- );
- };
-
- const throwCheckbox = optionsMenuDom.querySelector(
- '#jasmine-throw-failures'
- );
- throwCheckbox.checked = config.stopSpecOnExpectationFailure;
- throwCheckbox.onclick = () => {
- this.#navigateWithNewParam(
- 'stopSpecOnExpectationFailure',
- !config.stopSpecOnExpectationFailure
- );
- };
-
- const randomCheckbox = optionsMenuDom.querySelector(
- '#jasmine-random-order'
- );
- randomCheckbox.checked = config.random;
- randomCheckbox.onclick = () => {
- this.#navigateWithNewParam('random', !config.random);
- };
-
- if (!this.#omitHideDisabled) {
- const hideDisabled = optionsMenuDom.querySelector(
- '#jasmine-hide-disabled'
- );
- hideDisabled.checked = config.hideDisabled;
- hideDisabled.onclick = () => {
- this.#navigateWithNewParam('hideDisabled', !config.hideDisabled);
- };
- }
-
- const optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'),
- optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'),
- isOpen = /\bjasmine-open\b/;
-
- optionsTrigger.onclick = function() {
- if (isOpen.test(optionsPayload.className)) {
- optionsPayload.className = optionsPayload.className.replace(
- isOpen,
- ''
- );
- } else {
- optionsPayload.className += ' jasmine-open';
- }
- };
-
- return optionsMenuDom;
- }
- }
-
- return Banner;
-};
-
-jasmineRequire.FailuresView = function(j$) {
- 'use strict';
-
- const { createDom } = j$.private.htmlReporterUtils;
-
- class FailuresView {
- #urlBuilder;
- #failureEls;
- #showing;
-
- constructor(urlBuilder) {
- this.#urlBuilder = urlBuilder;
- this.#failureEls = [];
- this.#showing = false;
- this.rootEl = createDom(
- 'div',
- { className: 'jasmine-results' },
- createDom('div', { className: 'jasmine-failures' })
- );
- }
-
- append(result, parent) {
- // TODO: Figure out why the result is wrong if we build the DOM node later
- const el = this.#makeFailureEl(result, parent);
-
- if (this.#showing) {
- this.rootEl.querySelector('.jasmine-failures').appendChild(el);
- } else {
- this.#failureEls.push(el);
- }
- }
-
- show() {
- const failureNode = this.rootEl.querySelector('.jasmine-failures');
-
- for (const el of this.#failureEls) {
- failureNode.appendChild(el);
- }
-
- this.#showing = true;
- }
-
- #makeFailureEl(result, parent) {
- const failure = createDom(
- 'div',
- { className: 'jasmine-spec-detail jasmine-failed' },
- this.#failureDescription(result, parent),
- createDom('div', { className: 'jasmine-messages' })
- );
- const messages = failure.childNodes[1];
-
- for (let i = 0; i < result.failedExpectations.length; i++) {
- const expectation = result.failedExpectations[i];
- messages.appendChild(
- createDom(
- 'div',
- { className: 'jasmine-result-message' },
- expectation.message
- )
- );
- messages.appendChild(
- createDom(
- 'div',
- { className: 'jasmine-stack-trace' },
- expectation.stack
- )
- );
- }
-
- if (result.failedExpectations.length === 0) {
- messages.appendChild(
- createDom(
- 'div',
- { className: 'jasmine-result-message' },
- 'Spec has no expectations'
- )
- );
- }
-
- if (result.debugLogs) {
- messages.appendChild(this.#debugLogTable(result.debugLogs));
- }
-
- return failure;
- }
-
- #failureDescription(result, suite) {
- const wrapper = createDom(
- 'div',
- { className: 'jasmine-description' },
- createDom(
- 'a',
- {
- title: result.description,
- href: this.#urlBuilder.specHref(result)
- },
- result.description
- )
- );
- let suiteLink;
-
- while (suite && suite.parent) {
- wrapper.insertBefore(
- document.createTextNode(' > '),
- wrapper.firstChild
- );
- suiteLink = createDom(
- 'a',
- { href: this.#urlBuilder.suiteHref(suite) },
- suite.result.description
- );
- wrapper.insertBefore(suiteLink, wrapper.firstChild);
-
- suite = suite.parent;
- }
-
- return wrapper;
- }
-
- #debugLogTable(debugLogs) {
- const tbody = createDom('tbody');
-
- for (const entry of debugLogs) {
- tbody.appendChild(
- createDom(
- 'tr',
- {},
- createDom('td', {}, entry.timestamp.toString()),
- createDom(
- 'td',
- { className: 'jasmine-debug-log-msg' },
- entry.message
- )
- )
- );
- }
-
- return createDom(
- 'div',
- { className: 'jasmine-debug-log' },
- createDom(
- 'div',
- { className: 'jasmine-debug-log-header' },
- 'Debug logs'
- ),
- createDom(
- 'table',
- {},
- createDom(
- 'thead',
- {},
- createDom(
- 'tr',
- {},
- createDom('th', {}, 'Time (ms)'),
- createDom('th', {}, 'Message')
- )
- ),
- tbody
- )
- );
- }
- }
-
- return FailuresView;
-};
-
-jasmineRequire.htmlReporterUtils = function(j$) {
- 'use strict';
-
- function createDom(type, attrs, childrenArrayOrVarArgs) {
- const el = document.createElement(type);
- let children;
-
- if (Array.isArray(childrenArrayOrVarArgs)) {
- children = childrenArrayOrVarArgs;
- } else {
- children = [];
-
- for (let i = 2; i < arguments.length; i++) {
- children.push(arguments[i]);
- }
- }
-
- for (let i = 0; i < children.length; i++) {
- const child = children[i];
-
- if (typeof child === 'string') {
- el.appendChild(document.createTextNode(child));
- } else {
- if (child) {
- el.appendChild(child);
- }
- }
- }
-
- for (const attr in attrs) {
- if (attr === 'className') {
- el[attr] = attrs[attr];
- } else {
- el.setAttribute(attr, attrs[attr]);
- }
- }
-
- return el;
- }
-
- function noExpectations(result) {
- const allExpectations =
- result.failedExpectations.length + result.passedExpectations.length;
-
- return (
- allExpectations === 0 &&
- (result.status === 'passed' || result.status === 'failed')
- );
- }
-
- return { createDom, noExpectations };
-};
-
-jasmineRequire.HtmlReporterV2 = function(j$) {
- 'use strict';
-
- const { createDom, noExpectations } = j$.private.htmlReporterUtils;
-
- const specListTabId = 'jasmine-specListTab';
- const failuresTabId = 'jasmine-failuresTab';
- const perfTabId = 'jasmine-perfTab';
-
- /**
- * @class HtmlReporterV2
- * @classdesc Displays results and allows re-running individual specs and suites.
- * @implements {Reporter}
- * @param options Options object
- * @since 6.0.0
- * @example
- * const env = jasmine.getEnv();
- * const urls = new jasmine.HtmlReporterV2Urls();
- * const reporter = new jasmine.HtmlReporterV2({
- * env,
- * urls,
- * // container is optional and defaults to document.body.
- * container: someElement
- * });
- */
- class HtmlReporterV2 {
- #container;
- #queryString;
- #urlBuilder;
- #filterSpecs;
- #stateBuilder;
- #config;
- #htmlReporterMain;
-
- // Sub-views
- #alerts;
- #statusBar;
- #tabBar;
- #progress;
- #banner;
- #failures;
-
- constructor(options) {
- this.#container = options.container || document.body;
- this.#queryString =
- options.queryString ||
- new j$.QueryString({
- getWindowLocation() {
- return window.location;
- }
- });
- this.#urlBuilder = new UrlBuilder({
- queryString: this.#queryString,
- getSuiteById: id => this.#stateBuilder.suitesById[id]
- });
- this.#filterSpecs = options.urls.filteringSpecs();
-
- this.#config = options.env ? options.env.configuration() : {};
-
- this.#stateBuilder = new j$.private.ResultsStateBuilder();
-
- this.#alerts = new j$.private.AlertsView(this.#urlBuilder);
- this.#statusBar = new j$.private.OverallStatusBar(this.#urlBuilder);
- this.#statusBar.showRunning();
- this.#alerts.addBar(this.#statusBar.rootEl);
-
- this.#tabBar = new j$.private.TabBar(
- [
- { id: specListTabId, label: 'Spec List' },
- { id: failuresTabId, label: 'Failures' },
- { id: perfTabId, label: 'Performance' }
- ],
- tabId => {
- if (tabId === specListTabId) {
- this.#setMenuModeTo('jasmine-spec-list');
- } else if (tabId === failuresTabId) {
- this.#setMenuModeTo('jasmine-failure-list');
- } else {
- this.#setMenuModeTo('jasmine-performance');
- }
- }
- );
- this.#alerts.addBar(this.#tabBar.rootEl);
-
- this.#progress = new ProgressView();
- this.#banner = new j$.private.Banner(
- this.#queryString.navigateWithNewParam.bind(this.#queryString),
- true
- );
- this.#failures = new j$.private.FailuresView(this.#urlBuilder);
- this.#htmlReporterMain = createDom(
- 'div',
- { className: 'jasmine_html-reporter' },
- this.#banner.rootEl,
- this.#progress.rootEl,
- this.#alerts.rootEl,
- this.#failures.rootEl
- );
- this.#container.appendChild(this.#htmlReporterMain);
- this.#failures.show();
- }
-
- jasmineStarted(options) {
- this.#stateBuilder.jasmineStarted(options);
- this.#progress.start(
- options.totalSpecsDefined - options.numExcludedSpecs
- );
- }
-
- suiteStarted(result) {
- this.#stateBuilder.suiteStarted(result);
- }
-
- suiteDone(result) {
- this.#stateBuilder.suiteDone(result);
-
- if (result.status === 'failed') {
- this.#failures.append(result, this.#stateBuilder.currentParent);
- this.#statusBar.showFailing();
- }
- }
-
- specDone(result) {
- this.#stateBuilder.specDone(result);
- this.#progress.specDone(result, this.#config);
-
- if (noExpectations(result)) {
- const noSpecMsg = "Spec '" + result.fullName + "' has no expectations.";
- if (result.status === 'failed') {
- // eslint-disable-next-line no-console
- console.error(noSpecMsg);
- } else {
- // eslint-disable-next-line no-console
- console.warn(noSpecMsg);
- }
- }
-
- if (result.status === 'failed') {
- this.#failures.append(result, this.#stateBuilder.currentParent);
- this.#statusBar.showFailing();
- }
- }
-
- jasmineDone(doneResult) {
- this.#stateBuilder.jasmineDone(doneResult);
- this.#progress.rootEl.style.visibility = 'hidden';
- this.#banner.showOptionsMenu(this.#config);
-
- if (
- this.#stateBuilder.specsExecuted < this.#stateBuilder.totalSpecsDefined
- ) {
- this.#alerts.addSkipped(
- this.#stateBuilder.specsExecuted,
- this.#stateBuilder.totalSpecsDefined
- );
- }
-
- this.#statusBar.showDone(doneResult, this.#stateBuilder);
-
- if (doneResult.failedExpectations) {
- for (const f of doneResult.failedExpectations) {
- this.#alerts.addGlobalFailure(f);
- }
- }
-
- for (const dw of this.#stateBuilder.deprecationWarnings) {
- this.#alerts.addDeprecationWarning(dw);
- }
-
- const results = this.#find('.jasmine-results');
- const summary = new j$.private.SummaryTreeView(
- this.#urlBuilder,
- this.#filterSpecs
- );
- summary.addResults(this.#stateBuilder.topResults);
- results.appendChild(summary.rootEl);
- const perf = new j$.private.PerformanceView();
- perf.addResults(this.#stateBuilder.topResults);
- results.appendChild(perf.rootEl);
- this.#tabBar.showTab(specListTabId);
- this.#tabBar.showTab(perfTabId);
-
- if (this.#stateBuilder.anyNonTopSuiteFailures) {
- this.#tabBar.showTab(failuresTabId);
- this.#tabBar.selectTab(failuresTabId);
- } else {
- this.#tabBar.selectTab(specListTabId);
- }
- }
-
- #find(selector) {
- return this.#container.querySelector(
- '.jasmine_html-reporter ' + selector
- );
- }
-
- #setMenuModeTo(mode) {
- this.#htmlReporterMain.setAttribute(
- 'class',
- 'jasmine_html-reporter ' + mode
- );
- }
- }
-
- class ProgressView {
- constructor() {
- this.rootEl = createDom('progress', { value: 0 });
- }
-
- start(totalSpecsDefined) {
- this.rootEl.max = totalSpecsDefined;
- }
-
- specDone(result) {
- if (result.status !== 'excluded') {
- this.rootEl.value = this.rootEl.value + 1;
- }
-
- if (result.status === 'failed') {
- this.rootEl.classList.add('failed');
- }
- }
- }
-
- class UrlBuilder {
- #queryString;
- #getSuiteById;
-
- constructor(options) {
- this.#queryString = options.queryString;
- this.#getSuiteById = options.getSuiteById;
- }
-
- suiteHref(suite) {
- const path = this.#suitePath(suite);
- return this.#specPathHref(path);
- }
-
- specHref(specResult) {
- const suite = this.#getSuiteById(specResult.parentSuiteId);
- const path = this.#suitePath(suite);
- path.push(specResult.description);
- return this.#specPathHref(path);
- }
-
- runAllHref() {
- return this.#addToExistingQueryString('path', '');
- }
-
- seedHref(seed) {
- return this.#addToExistingQueryString('seed', seed);
- }
-
- #suitePath(suite) {
- const path = [];
-
- while (suite && suite.parent) {
- path.unshift(suite.result.description);
- suite = suite.parent;
- }
-
- return path;
- }
-
- #specPathHref(specPath) {
- return this.#addToExistingQueryString('path', JSON.stringify(specPath));
- }
-
- #addToExistingQueryString(k, v) {
- // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
- return (
- (window.location.pathname || '') +
- this.#queryString.fullStringWithNewParam(k, v)
- );
- }
- }
-
- return HtmlReporterV2;
-};
-
-jasmineRequire.HtmlReporterV2Urls = function(j$) {
- 'use strict';
-
- // TODO unify with V2 UrlBuilder?
- /**
- * @class HtmlReporterV2Urls
- * @classdesc Processes URLs for {@link HtmlReporterV2}.
- * @since 6.0.0
- */
- class HtmlReporterV2Urls {
- constructor(options = {}) {
- // queryString is injectable for use in our own tests, but user code will
- // not pass any options.
- this.queryString =
- options.queryString ||
- new jasmine.QueryString({
- getWindowLocation: function() {
- return window.location;
- }
- });
- }
-
- /**
- * Creates a {@link Configuration} from the current page's URL. Supported
- * query string parameters include all those set by {@link HtmlReporterV2}
- * as well as spec=partialPath, which filters out specs whose paths don't
- * contain partialPath.
- * @returns {Configuration}
- * @example
- * const urls = new jasmine.HtmlReporterV2Urls();
- * env.configure(urls.configFromCurrentUrl());
- */
- configFromCurrentUrl() {
- const config = {
- stopOnSpecFailure: this.queryString.getParam('stopOnSpecFailure'),
- stopSpecOnExpectationFailure: this.queryString.getParam(
- 'stopSpecOnExpectationFailure'
- )
- };
-
- const random = this.queryString.getParam('random');
-
- if (random !== undefined && random !== '') {
- config.random = random;
- }
-
- const seed = this.queryString.getParam('seed');
- if (seed) {
- config.seed = seed;
- }
-
- const specFilter = new j$.private.HtmlSpecFilterV2({
- filterParams: () => ({
- path: this.queryString.getParam('path'),
- spec: this.queryString.getParam('spec')
- })
- });
-
- config.specFilter = function(spec) {
- return specFilter.matches(spec);
- };
-
- return config;
- }
-
- filteringSpecs() {
- return !!this.queryString.getParam('path');
- }
- }
-
- return HtmlReporterV2Urls;
-};
-
-jasmineRequire.HtmlSpecFilterV2 = function() {
- class HtmlSpecFilterV2 {
- #getFilterParams;
-
- constructor(options) {
- this.#getFilterParams = options.filterParams;
- }
-
- matches(spec) {
- const params = this.#getFilterParams();
-
- if (params.path) {
- return this.#matchesPath(spec, JSON.parse(params.path));
- } else if (params.spec) {
- // Like legacy HtmlSpecFilter, retained because it's convenient for
- // hand-constructing filter URLs
- return spec.getFullName().includes(params.spec);
- }
-
- return true;
- }
-
- #matchesPath(spec, path) {
- const specPath = spec.getPath();
-
- if (path.length > specPath.length) {
- return false;
- }
-
- for (let i = 0; i < path.length; i++) {
- if (specPath[i] !== path[i]) {
- return false;
- }
- }
-
- return true;
- }
- }
-
- return HtmlSpecFilterV2;
-};
-
-jasmineRequire.OverallStatusBar = function(j$) {
- 'use strict';
-
- const { createDom } = j$.private.htmlReporterUtils;
- const staticClassNames = 'jasmine-overall-result jasmine-bar';
-
- class OverallStatusBar {
- #urlBuilder;
-
- constructor(urlBuilder) {
- this.#urlBuilder = urlBuilder;
- this.rootEl = createDom('span', {
- className: staticClassNames,
- 'aria-live': 'polite'
- });
- }
-
- showRunning() {
- this.rootEl.textContent = 'Running...';
- this.rootEl.classList.add('jasmine-in-progress');
- }
-
- showFailing() {
- this.rootEl.textContent = 'Failing...';
- this.rootEl.classList.add('jasmine-failed');
- }
-
- showDone(doneResult, stateBuilder) {
- // Clear any classes added to represent in-progress state
- this.rootEl.className = staticClassNames;
-
- let statusBarMessage = '';
- const globalFailures =
- (doneResult && doneResult.failedExpectations) || [];
- const failed = stateBuilder.failureCount + globalFailures.length > 0;
-
- if (stateBuilder.totalSpecsDefined > 0 || failed) {
- statusBarMessage +=
- pluralize('spec', stateBuilder.specsExecuted) +
- ', ' +
- pluralize('failure', stateBuilder.failureCount);
- if (stateBuilder.pendingSpecCount) {
- statusBarMessage +=
- ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount);
- }
- }
-
- if (doneResult.overallStatus === 'passed') {
- this.rootEl.classList.add('jasmine-passed');
- } else if (doneResult.overallStatus === 'incomplete') {
- this.rootEl.classList.add('jasmine-incomplete');
- statusBarMessage =
- 'Incomplete: ' +
- doneResult.incompleteReason +
- ', ' +
- statusBarMessage;
- } else {
- this.rootEl.classList.add('jasmine-failed');
- }
-
- // Replace any existing children with the message
- this.rootEl.textContent = statusBarMessage;
-
- const order = doneResult.order;
- if (order && order.random) {
- this.#addSeedBar(order);
- }
-
- this.#addDuration(doneResult);
- }
-
- #addSeedBar(order) {
- this.rootEl.appendChild(
- createDom(
- 'span',
- { className: 'jasmine-seed-bar' },
- ', randomized with seed ',
- createDom(
- 'a',
- {
- title: 'randomized with seed ' + order.seed,
- href: this.#urlBuilder.seedHref(order.seed)
- },
- order.seed
- )
- )
- );
- }
-
- #addDuration(doneResult) {
- const secs = doneResult.totalTime / 1000;
- this.rootEl.appendChild(
- createDom(
- 'span',
- { className: 'jasmine-duration' },
- `finished in ${secs}s`
- )
- );
- }
- }
-
- function pluralize(singular, count) {
- const word = count === 1 ? singular : singular + 's';
- return '' + count + ' ' + word;
- }
-
- return OverallStatusBar;
-};
-
-jasmineRequire.PerformanceView = function(j$) {
- const createDom = j$.private.htmlReporterUtils.createDom;
- const MAX_SLOW_SPECS = 20;
-
- class PerformanceView {
- #summary;
- #tbody;
-
- constructor() {
- this.#tbody = document.createElement('tbody');
- this.#summary = document.createElement('div');
- this.rootEl = createDom(
- 'div',
- { className: 'jasmine-performance-view' },
- createDom('h2', {}, 'Performance'),
- this.#summary,
- createDom('h3', {}, 'Slowest Specs'),
- createDom(
- 'table',
- {},
- createDom(
- 'thead',
- {},
- createDom(
- 'tr',
- {},
- createDom('th', {}, 'Duration'),
- createDom('th', {}, 'Spec Name')
- )
- ),
- this.#tbody
- )
- );
- }
-
- addResults(resultsTree) {
- const specResults = [];
- getSpecResults(resultsTree, specResults);
-
- if (specResults.length === 0) {
- return;
- }
-
- specResults.sort(function(a, b) {
- if (a.duration < b.duration) {
- return 1;
- } else if (a.duration > b.duration) {
- return -1;
- } else {
- return 0;
- }
- });
-
- this.#populateSumary(specResults);
- this.#populateTable(specResults);
- }
-
- #populateSumary(specResults) {
- const total = specResults.map(r => r.duration).reduce((a, b) => a + b, 0);
- const mean = total / specResults.length;
- const median = specResults[Math.floor(specResults.length / 2)].duration;
- this.#summary.appendChild(
- document.createTextNode(`Mean spec run time: ${mean.toFixed(0)}ms`)
- );
- this.#summary.appendChild(document.createElement('br'));
- this.#summary.appendChild(
- document.createTextNode(`Median spec run time: ${median}ms`)
- );
- }
-
- #populateTable(specResults) {
- specResults = specResults.slice(0, MAX_SLOW_SPECS);
-
- for (const r of specResults) {
- this.#tbody.appendChild(
- createDom(
- 'tr',
- {},
- createDom('td', {}, `${r.duration}ms`),
- createDom('td', {}, r.fullName)
- )
- );
- }
- }
- }
-
- function getSpecResults(resultsTree, dest) {
- for (const node of resultsTree.children) {
- if (node.type === 'suite') {
- getSpecResults(node, dest);
- } else if (node.result.status !== 'excluded') {
- dest.push(node.result);
- }
- }
- }
-
- return PerformanceView;
-};
-
-jasmineRequire.ResultsStateBuilder = function(j$) {
- 'use strict';
-
- class ResultsStateBuilder {
- constructor() {
- this.topResults = new j$.private.ResultsNode({}, '', null);
- this.currentParent = this.topResults;
- this.suitesById = {};
- this.totalSpecsDefined = 0;
- this.specsExecuted = 0;
- this.failureCount = 0;
- this.anyNonTopSuiteFailures = false;
- this.pendingSpecCount = 0;
- this.deprecationWarnings = [];
- }
-
- suiteStarted(result) {
- this.currentParent.addChild(result, 'suite');
- this.currentParent = this.currentParent.last();
- this.suitesById[result.id] = this.currentParent;
- }
-
- suiteDone(result) {
- this.currentParent.updateResult(result);
- this.#addDeprecationWarnings(result, 'suite');
-
- if (this.currentParent !== this.topResults) {
- this.currentParent = this.currentParent.parent;
- }
-
- if (result.status === 'failed') {
- this.failureCount++;
- this.anyNonTopSuiteFailures = true;
- }
- }
-
- specDone(result) {
- this.currentParent.addChild(result, 'spec');
- this.#addDeprecationWarnings(result, 'spec');
-
- if (result.status !== 'excluded') {
- this.specsExecuted++;
- }
-
- if (result.status === 'failed') {
- this.failureCount++;
- this.anyNonTopSuiteFailures = true;
- }
-
- if (result.status == 'pending') {
- this.pendingSpecCount++;
- }
- }
-
- jasmineStarted(result) {
- this.totalSpecsDefined = result.totalSpecsDefined;
- }
-
- jasmineDone(result) {
- if (result.failedExpectations) {
- this.failureCount += result.failedExpectations.length;
- }
-
- this.#addDeprecationWarnings(result);
- }
-
- #addDeprecationWarnings(result, runnableType) {
- if (result.deprecationWarnings) {
- for (const dw of result.deprecationWarnings) {
- this.deprecationWarnings.push({
- message: dw.message,
- stack: dw.stack,
- runnableName: result.fullName,
- runnableType: runnableType
- });
- }
- }
- }
- }
-
- return ResultsStateBuilder;
-};
-
-jasmineRequire.SummaryTreeView = function(j$) {
- 'use strict';
-
- const { createDom, noExpectations } = j$.private.htmlReporterUtils;
-
- class SummaryTreeView {
- #urlBuilder;
- #filterSpecs;
-
- constructor(urlBuilder, filterSpecs) {
- this.#urlBuilder = urlBuilder;
- this.#filterSpecs = filterSpecs;
- this.rootEl = createDom('div', {
- className: 'jasmine-summary'
- });
- }
-
- addResults(resultsTree) {
- this.#addResults(resultsTree, this.rootEl);
- }
-
- #addResults(resultsTree, domParent) {
- let specListNode;
- for (let i = 0; i < resultsTree.children.length; i++) {
- const resultNode = resultsTree.children[i];
- if (this.#filterSpecs && !hasActiveSpec(resultNode)) {
- continue;
- }
- if (resultNode.type === 'suite') {
- const suiteListNode = createDom(
- 'ul',
- { className: 'jasmine-suite', id: 'suite-' + resultNode.result.id },
- createDom(
- 'li',
- {
- className:
- 'jasmine-suite-detail jasmine-' + resultNode.result.status
- },
- createDom(
- 'a',
- { href: this.#urlBuilder.specHref(resultNode.result) },
- resultNode.result.description
- )
- )
- );
-
- this.#addResults(resultNode, suiteListNode);
- domParent.appendChild(suiteListNode);
- }
- if (resultNode.type === 'spec') {
- if (domParent.getAttribute('class') !== 'jasmine-specs') {
- specListNode = createDom('ul', {
- className: 'jasmine-specs'
- });
- domParent.appendChild(specListNode);
- }
- let specDescription = resultNode.result.description;
- if (noExpectations(resultNode.result)) {
- specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
- }
- if (resultNode.result.status === 'pending') {
- if (resultNode.result.pendingReason !== '') {
- specDescription +=
- ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
- } else {
- specDescription += ' PENDING';
- }
- }
- specListNode.appendChild(
- createDom(
- 'li',
- {
- className: 'jasmine-' + resultNode.result.status,
- id: 'spec-' + resultNode.result.id
- },
- createDom(
- 'a',
- { href: this.#urlBuilder.specHref(resultNode.result) },
- specDescription
- ),
- createDom(
- 'span',
- { className: 'jasmine-spec-duration' },
- '(' + resultNode.result.duration + 'ms)'
- )
- )
- );
- }
- }
- }
- }
-
- function hasActiveSpec(resultNode) {
- if (resultNode.type === 'spec' && resultNode.result.status !== 'excluded') {
- return true;
- }
-
- if (resultNode.type === 'suite') {
- for (let i = 0, j = resultNode.children.length; i < j; i++) {
- if (hasActiveSpec(resultNode.children[i])) {
- return true;
- }
- }
- }
- }
-
- return SummaryTreeView;
-};
-
-jasmineRequire.SymbolsView = function(j$) {
- 'use strict';
-
- const { createDom, noExpectations } = j$.private.htmlReporterUtils;
-
- class SymbolsView {
- constructor() {
- this.rootEl = createDom('ul', {
- className: 'jasmine-symbol-summary'
- });
- }
-
- append(result, config) {
- this.rootEl.appendChild(
- createDom('li', {
- className: this.#className(result, config),
- id: 'spec_' + result.id,
- title: result.fullName
- })
- );
- }
-
- #className(result, config) {
- if (noExpectations(result) && result.status === 'passed') {
- return 'jasmine-empty';
- } else if (result.status === 'excluded') {
- if (config.hideDisabled) {
- return 'jasmine-excluded-no-display';
- } else {
- return 'jasmine-excluded';
- }
- } else {
- return 'jasmine-' + result.status;
- }
- }
- }
-
- return SymbolsView;
-};
-
-jasmineRequire.TabBar = function(j$) {
- const createDom = j$.private.htmlReporterUtils.createDom;
-
- class TabBar {
- #tabs;
- #onSelectTab;
-
- // tabSpecs should be an array of {id, label}.
- // All tabs are initially not visible and not selected.
- constructor(tabSpecs, onSelectTab) {
- this.#onSelectTab = onSelectTab;
- this.#tabs = [];
- this.#tabs = tabSpecs.map(ts => new Tab(ts, () => this.selectTab(ts.id)));
-
- this.rootEl = createDom(
- 'span',
- { className: 'jasmine-menu jasmine-bar' },
- this.#tabs.map(t => t.rootEl)
- );
- }
-
- showTab(id) {
- for (const tab of this.#tabs) {
- if (tab.rootEl.id === id) {
- tab.setVisibility(true);
- }
- }
- }
-
- selectTab(id) {
- for (const tab of this.#tabs) {
- tab.setSelected(tab.rootEl.id === id);
- }
-
- this.#onSelectTab(id);
- }
- }
-
- class Tab {
- #spec;
- #onClick;
-
- constructor(spec, onClick) {
- this.#spec = spec;
- this.#onClick = onClick;
- this.rootEl = createDom(
- 'span',
- { id: spec.id, className: 'jasmine-tab jasmine-hidden' },
- this.#createLink()
- );
- }
-
- setVisibility(visible) {
- this.rootEl.classList.toggle('jasmine-hidden', !visible);
- }
-
- setSelected(selected) {
- if (selected) {
- this.rootEl.textContent = this.#spec.label;
- } else {
- this.rootEl.textContent = '';
- this.rootEl.appendChild(this.#createLink());
- }
- }
-
- #createLink() {
- const link = createDom('a', { href: '#' }, this.#spec.label);
- link.addEventListener('click', e => {
- e.preventDefault();
- this.#onClick();
- });
- return link;
- }
- }
-
- return TabBar;
-};
diff --git a/src/static/tests/lib/jasmine-6.0.1/jasmine.css b/src/static/tests/lib/jasmine-6.0.1/jasmine.css
deleted file mode 100644
index 3be6101..0000000
--- a/src/static/tests/lib/jasmine-6.0.1/jasmine.css
+++ /dev/null
@@ -1,351 +0,0 @@
-@charset "UTF-8";
-body {
- overflow-y: scroll;
-}
-
-.jasmine_html-reporter {
- width: 100%;
- background-color: #eee;
- padding: 5px;
- margin: -8px;
- font-size: 12px;
- font-family: Monaco, "Lucida Console", monospace;
- line-height: 14px;
- color: #333;
-}
-.jasmine_html-reporter a {
- text-decoration: none;
-}
-.jasmine_html-reporter a:hover {
- text-decoration: underline;
-}
-.jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 {
- margin: 0;
- line-height: 14px;
-}
-.jasmine_html-reporter .jasmine-banner,
-.jasmine_html-reporter .jasmine-symbol-summary,
-.jasmine_html-reporter .jasmine-summary,
-.jasmine_html-reporter .jasmine-result-message,
-.jasmine_html-reporter .jasmine-spec .jasmine-description,
-.jasmine_html-reporter .jasmine-spec-detail .jasmine-description,
-.jasmine_html-reporter .jasmine-alert .jasmine-bar,
-.jasmine_html-reporter .jasmine-stack-trace {
- padding-left: 9px;
- padding-right: 9px;
-}
-.jasmine_html-reporter .jasmine-banner {
- position: relative;
-}
-.jasmine_html-reporter .jasmine-banner .jasmine-title {
- background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==") no-repeat;
- background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=") no-repeat, none;
- background-size: 100%;
- display: block;
- float: left;
- width: 90px;
- height: 25px;
-}
-.jasmine_html-reporter .jasmine-banner .jasmine-version {
- margin-left: 14px;
- position: relative;
- top: 6px;
-}
-.jasmine_html-reporter #jasmine_content {
- position: fixed;
- right: 100%;
-}
-.jasmine_html-reporter .jasmine-banner {
- margin-top: 14px;
-}
-.jasmine_html-reporter .jasmine-duration {
- color: #fff;
- float: right;
- line-height: 28px;
- padding-right: 9px;
- font-size: 12px;
-}
-.jasmine_html-reporter .jasmine-symbol-summary {
- overflow: hidden;
- margin: 14px 0;
-}
-.jasmine_html-reporter .jasmine-symbol-summary li {
- display: inline-block;
- height: 10px;
- width: 14px;
- font-size: 16px;
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed {
- font-size: 14px;
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before {
- color: #007069;
- content: "•";
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed {
- line-height: 9px;
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before {
- color: #ca3a11;
- content: "×";
- font-weight: bold;
- margin-left: -1px;
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded {
- font-size: 14px;
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded:before {
- color: #bababa;
- content: "•";
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded-no-display {
- font-size: 14px;
- display: none;
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending {
- line-height: 17px;
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before {
- color: #ba9d37;
- content: "*";
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty {
- font-size: 14px;
-}
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before {
- color: #ba9d37;
- content: "•";
-}
-.jasmine_html-reporter progress {
- width: 100%;
-}
-.jasmine_html-reporter progress[value] {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
-}
-.jasmine_html-reporter progress[value]::-webkit-progress-value, .jasmine_html-reporter progress[value]::-moz-progress-bar {
- background: #007069;
-}
-.failed .jasmine_html-reporter progress[value]::-webkit-progress-value, .failed .jasmine_html-reporter progress[value]::-moz-progress-bar {
- background: #ca3a11;
-}
-.jasmine_html-reporter progress.failed[value]::-webkit-progress-value, .jasmine_html-reporter progress.failed[value]::-moz-progress-bar {
- background: #ca3a11;
-}
-.jasmine_html-reporter .jasmine-run-options {
- float: right;
- margin-right: 5px;
- border: 1px solid #8a4182;
- color: #8a4182;
- position: relative;
- line-height: 20px;
-}
-.jasmine_html-reporter .jasmine-run-options .jasmine-trigger {
- cursor: pointer;
- padding: 8px 16px;
-}
-.jasmine_html-reporter .jasmine-run-options .jasmine-payload {
- position: absolute;
- display: none;
- right: -1px;
- border: 1px solid #8a4182;
- background-color: #eee;
- white-space: nowrap;
- padding: 4px 8px;
-}
-.jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open {
- display: block;
-}
-.jasmine_html-reporter .jasmine-bar {
- line-height: 28px;
- font-size: 14px;
- display: block;
- color: #eee;
-}
-.jasmine_html-reporter .jasmine-bar.jasmine-in-progress {
- color: #333;
-}
-.jasmine_html-reporter .jasmine-bar.jasmine-failed, .jasmine_html-reporter .jasmine-bar.jasmine-errored {
- background-color: #ca3a11;
- color: #eee;
- border-bottom: 1px solid #eee;
-}
-.jasmine_html-reporter .jasmine-bar.jasmine-passed {
- background-color: #007069;
-}
-.jasmine_html-reporter .jasmine-bar.jasmine-incomplete {
- background-color: #bababa;
-}
-.jasmine_html-reporter .jasmine-bar.jasmine-skipped {
- background-color: #bababa;
-}
-.jasmine_html-reporter .jasmine-bar.jasmine-warning {
- margin-top: 14px;
- margin-bottom: 14px;
- background-color: #ba9d37;
- color: #333;
-}
-.jasmine_html-reporter .jasmine-bar.jasmine-menu {
- background-color: #fff;
- color: #000;
-}
-.jasmine_html-reporter .jasmine-bar.jasmine-menu a {
- color: blue;
- text-decoration: underline;
-}
-.jasmine_html-reporter .jasmine-bar a {
- color: white;
-}
-.jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list,
-.jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures,
-.jasmine_html-reporter.jasmine-spec-list .jasmine-performance-view {
- display: none;
-}
-.jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list,
-.jasmine_html-reporter.jasmine-failure-list .jasmine-summary,
-.jasmine_html-reporter.jasmine-failure-list .jasmine-performance-view {
- display: none;
-}
-.jasmine_html-reporter.jasmine-performance .jasmine-results .jasmine-failures,
-.jasmine_html-reporter.jasmine-performance .jasmine-summary {
- display: none;
-}
-.jasmine_html-reporter .jasmine-results {
- margin-top: 14px;
-}
-.jasmine_html-reporter .jasmine-summary {
- margin-top: 14px;
-}
-.jasmine_html-reporter .jasmine-summary ul {
- list-style-type: none;
- margin-left: 14px;
- padding-top: 0;
- padding-left: 0;
-}
-.jasmine_html-reporter .jasmine-summary ul.jasmine-suite {
- margin-top: 7px;
- margin-bottom: 7px;
-}
-.jasmine_html-reporter .jasmine-summary li.jasmine-passed a {
- color: #007069;
-}
-.jasmine_html-reporter .jasmine-summary li.jasmine-failed a {
- color: #ca3a11;
-}
-.jasmine_html-reporter .jasmine-summary li.jasmine-empty a {
- color: #ba9d37;
-}
-.jasmine_html-reporter .jasmine-summary li.jasmine-pending a {
- color: #ba9d37;
-}
-.jasmine_html-reporter .jasmine-summary li.jasmine-excluded a {
- color: #bababa;
-}
-.jasmine_html-reporter .jasmine-specs li.jasmine-passed a:before {
- content: "• ";
-}
-.jasmine_html-reporter .jasmine-specs li.jasmine-failed a:before {
- content: "× ";
-}
-.jasmine_html-reporter .jasmine-specs li.jasmine-empty a:before {
- content: "* ";
-}
-.jasmine_html-reporter .jasmine-specs li.jasmine-pending a:before {
- content: "• ";
-}
-.jasmine_html-reporter .jasmine-specs li.jasmine-excluded a:before {
- content: "• ";
-}
-.jasmine_html-reporter .jasmine-specs li .jasmine-spec-duration {
- margin-left: 1em;
-}
-.jasmine_html-reporter .jasmine-description + .jasmine-suite {
- margin-top: 0;
-}
-.jasmine_html-reporter .jasmine-suite {
- margin-top: 14px;
-}
-.jasmine_html-reporter .jasmine-suite a {
- color: #333;
-}
-.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail {
- margin-bottom: 28px;
-}
-.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description {
- background-color: #ca3a11;
- color: white;
-}
-.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a {
- color: white;
-}
-.jasmine_html-reporter .jasmine-result-message {
- padding-top: 14px;
- color: #333;
- white-space: pre-wrap;
-}
-.jasmine_html-reporter .jasmine-result-message span.jasmine-result {
- display: block;
-}
-.jasmine_html-reporter .jasmine-stack-trace {
- margin: 5px 0 0 0;
- max-height: 224px;
- overflow: auto;
- line-height: 18px;
- color: #666;
- border: 1px solid #ddd;
- background: white;
- white-space: pre;
-}
-.jasmine_html-reporter .jasmine-expander a {
- display: block;
- margin-left: 14px;
- color: blue;
- text-decoration: underline;
-}
-.jasmine_html-reporter .jasmine-expander-contents {
- display: none;
-}
-.jasmine_html-reporter .jasmine-expanded {
- padding-bottom: 10px;
-}
-.jasmine_html-reporter .jasmine-expanded .jasmine-expander-contents {
- display: block;
- margin-left: 14px;
- padding: 5px;
-}
-.jasmine_html-reporter .jasmine-debug-log {
- margin: 5px 0 0 0;
- padding: 5px;
- color: #666;
- border: 1px solid #ddd;
- background: white;
-}
-.jasmine_html-reporter .jasmine-debug-log table {
- border-spacing: 0;
-}
-.jasmine_html-reporter .jasmine-debug-log table, .jasmine_html-reporter .jasmine-debug-log th, .jasmine_html-reporter .jasmine-debug-log td {
- border: 1px solid #ddd;
-}
-.jasmine_html-reporter .jasmine-debug-log .jasmine-debug-log-msg {
- white-space: pre;
-}
-
-.jasmine-hidden {
- display: none;
-}
-
-.jasmine-tab + .jasmine-tab:before {
- content: " | ";
-}
-
-.jasmine-performance-view h2, .jasmine-performance-view h3 {
- margin-top: 1em;
- margin-bottom: 1em;
-}
-.jasmine-performance-view table {
- border-spacing: 5px;
-}
-.jasmine-performance-view th, .jasmine-performance-view td {
- text-align: left;
-}
\ No newline at end of file
diff --git a/src/static/tests/lib/jasmine-6.0.1/jasmine.js b/src/static/tests/lib/jasmine-6.0.1/jasmine.js
deleted file mode 100644
index 5d5700b..0000000
--- a/src/static/tests/lib/jasmine-6.0.1/jasmine.js
+++ /dev/null
@@ -1,12412 +0,0 @@
-/*
-Copyright (c) 2008-2019 Pivotal Labs
-Copyright (c) 2008-2026 The Jasmine developers
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-// eslint-disable-next-line no-unused-vars,no-var
-var getJasmineRequireObj = (function() {
- 'use strict';
- let jasmineRequire;
-
- if (
- typeof module !== 'undefined' &&
- module.exports &&
- typeof exports !== 'undefined'
- ) {
- // Node
- jasmineRequire = exports;
- } else {
- // Browser
- jasmineRequire = globalThis.jasmineRequire = {};
- }
-
- function getJasmineRequire() {
- return jasmineRequire;
- }
-
- const loadedAsBrowserEsm =
- globalThis.document && !globalThis.document.currentScript;
-
- getJasmineRequire().core = function(jRequire) {
- const j$ = {};
- Object.defineProperty(j$, 'private', {
- enumerable: true,
- value: {}
- });
-
- jRequire.base(j$, globalThis);
- j$.private.deprecateMonkeyPatching = jRequire.deprecateMonkeyPatching(j$);
- j$.private.util = jRequire.util(j$);
- j$.private.errors = jRequire.errors();
- j$.private.formatErrorMsg = jRequire.formatErrorMsg(j$);
- j$.private.AllOf = jRequire.AllOf(j$);
- j$.private.Any = jRequire.Any(j$);
- j$.private.Anything = jRequire.Anything(j$);
- j$.private.CallTracker = jRequire.CallTracker(j$);
- j$.private.MockDate = jRequire.MockDate(j$);
- j$.private.getStackClearer = jRequire.StackClearer(j$);
- j$.private.Clock = jRequire.Clock(j$);
- j$.private.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$);
- j$.private.Deprecator = jRequire.Deprecator(j$);
- j$.private.Configuration = jRequire.Configuration(j$);
- j$.private.Env = jRequire.Env(j$);
- j$.private.StackTrace = jRequire.StackTrace(j$);
- j$.private.ExceptionFormatter = jRequire.ExceptionFormatter(j$);
- j$.private.ExpectationFilterChain = jRequire.ExpectationFilterChain();
- j$.private.Expector = jRequire.Expector(j$);
- j$.private.Expectation = jRequire.Expectation(j$);
- j$.private.buildExpectationResult = jRequire.buildExpectationResult(j$);
- j$.private.JsApiReporter = jRequire.JsApiReporter(j$);
- j$.private.makePrettyPrinter = jRequire.makePrettyPrinter(j$);
- j$.private.basicPrettyPrinter = j$.private.makePrettyPrinter();
- j$.private.MatchersUtil = jRequire.MatchersUtil(j$);
- j$.private.ObjectContaining = jRequire.ObjectContaining(j$);
- j$.private.ArrayContaining = jRequire.ArrayContaining(j$);
- j$.private.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$);
- j$.private.MapContaining = jRequire.MapContaining(j$);
- j$.private.SetContaining = jRequire.SetContaining(j$);
- j$.private.QueueRunner = jRequire.QueueRunner(j$);
- j$.private.NeverSkipPolicy = jRequire.NeverSkipPolicy(j$);
- j$.private.SkipAfterBeforeAllErrorPolicy = jRequire.SkipAfterBeforeAllErrorPolicy(
- j$
- );
- j$.private.CompleteOnFirstErrorSkipPolicy = jRequire.CompleteOnFirstErrorSkipPolicy(
- j$
- );
- j$.private.reporterEvents = jRequire.reporterEvents(j$);
- j$.private.ReportDispatcher = jRequire.ReportDispatcher(j$);
- j$.ParallelReportDispatcher = jRequire.ParallelReportDispatcher(j$);
- j$.private.CurrentRunableTracker = jRequire.CurrentRunableTracker();
- j$.private.RunableResources = jRequire.RunableResources(j$);
- j$.private.Runner = jRequire.Runner(j$);
- j$.private.Spec = jRequire.Spec(j$);
- j$.private.Spy = jRequire.Spy(j$);
- j$.private.SpyFactory = jRequire.SpyFactory(j$);
- j$.private.SpyRegistry = jRequire.SpyRegistry(j$);
- j$.private.SpyStrategy = jRequire.SpyStrategy(j$);
- j$.private.StringMatching = jRequire.StringMatching(j$);
- j$.private.StringContaining = jRequire.StringContaining(j$);
- j$.private.UserContext = jRequire.UserContext(j$);
- j$.private.Suite = jRequire.Suite(j$);
- j$.private.SuiteBuilder = jRequire.SuiteBuilder(j$);
- j$.Timer = jRequire.Timer();
- j$.private.TreeProcessor = jRequire.TreeProcessor(j$);
- j$.private.TreeRunner = jRequire.TreeRunner(j$);
- j$.version = jRequire.version();
- j$.private.Order = jRequire.Order();
- j$.private.DiffBuilder = jRequire.DiffBuilder(j$);
- j$.private.NullDiffBuilder = jRequire.NullDiffBuilder(j$);
- j$.private.ObjectPath = jRequire.ObjectPath(j$);
- j$.private.MismatchTree = jRequire.MismatchTree(j$);
- j$.private.GlobalErrors = jRequire.GlobalErrors(j$);
- j$.private.Truthy = jRequire.Truthy(j$);
- j$.private.Falsy = jRequire.Falsy(j$);
- j$.private.Empty = jRequire.Empty(j$);
- j$.private.NotEmpty = jRequire.NotEmpty(j$);
- j$.private.Is = jRequire.Is(j$);
-
- j$.private.matchers = jRequire.requireMatchers(jRequire, j$);
- j$.private.asyncMatchers = jRequire.requireAsyncMatchers(jRequire, j$);
-
- j$.private.loadedAsBrowserEsm = loadedAsBrowserEsm;
-
- j$.private.deprecateMonkeyPatching(j$, [
- // These are meant to be set by users.
- 'DEFAULT_TIMEOUT_INTERVAL',
- 'MAX_PRETTY_PRINT_ARRAY_LENGTH',
- 'MAX_PRETTY_PRINT_CHARS',
- 'MAX_PRETTY_PRINT_DEPTH',
-
- // These are part of the deprecation warning mechanism. To avoid infinite
- // recursion, they're separately protected in a way that doesn't emit
- // deprecation warnings.
- 'private',
- 'getEnv'
- ]);
-
- return j$;
- };
-
- return getJasmineRequire;
-})(this);
-
-getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
- 'use strict';
-
- const availableMatchers = [
- 'nothing',
- 'toBe',
- 'toBeCloseTo',
- 'toBeDefined',
- 'toBeInstanceOf',
- 'toBeFalse',
- 'toBeFalsy',
- 'toBeGreaterThan',
- 'toBeGreaterThanOrEqual',
- 'toBeLessThan',
- 'toBeLessThanOrEqual',
- 'toBeNaN',
- 'toBeNegativeInfinity',
- 'toBeNull',
- 'toBePositiveInfinity',
- 'toBeTrue',
- 'toBeTruthy',
- 'toBeUndefined',
- 'toBeNullish',
- 'toContain',
- 'toEqual',
- 'toHaveSize',
- 'toHaveBeenCalled',
- 'toHaveBeenCalledBefore',
- 'toHaveBeenCalledOnceWith',
- 'toHaveBeenCalledTimes',
- 'toHaveBeenCalledWith',
- 'toHaveClass',
- 'toHaveClasses',
- 'toHaveSpyInteractions',
- 'toHaveNoOtherSpyInteractions',
- 'toMatch',
- 'toThrow',
- 'toThrowError',
- 'toThrowMatching'
- ],
- matchers = {};
-
- for (const name of availableMatchers) {
- matchers[name] = jRequire[name](j$);
- }
-
- return matchers;
-};
-
-getJasmineRequireObj().base = function(j$, jasmineGlobal) {
- 'use strict';
-
- /**
- * Maximum object depth the pretty printer will print to.
- * Set this to a lower value to speed up pretty printing if you have large objects.
- * @name jasmine.MAX_PRETTY_PRINT_DEPTH
- * @default 8
- * @since 1.3.0
- */
- j$.MAX_PRETTY_PRINT_DEPTH = 8;
- /**
- * Maximum number of array elements to display when pretty printing objects.
- * This will also limit the number of keys and values displayed for an object.
- * Elements past this number will be ellipised.
- * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH
- * @default 50
- * @since 2.7.0
- */
- j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50;
- /**
- * Maximum number of characters to display when pretty printing objects.
- * Characters past this number will be ellipised.
- * @name jasmine.MAX_PRETTY_PRINT_CHARS
- * @default 1000
- * @since 2.9.0
- */
- j$.MAX_PRETTY_PRINT_CHARS = 1000;
- /**
- * Default number of milliseconds Jasmine will wait for an asynchronous spec,
- * before, or after function to complete. This can be overridden on a case by
- * case basis by passing a time limit as the third argument to {@link it},
- * {@link beforeEach}, {@link afterEach}, {@link beforeAll}, or
- * {@link afterAll}. The value must be no greater than the largest number of
- * milliseconds supported by setTimeout, which is usually 2147483647.
- *
- * While debugging tests, you may want to set this to a large number (or pass
- * a large number to one of the functions mentioned above) so that Jasmine
- * does not move on to after functions or the next spec while you're debugging.
- * @name jasmine.DEFAULT_TIMEOUT_INTERVAL
- * @default 5000
- * @since 1.3.0
- */
- let DEFAULT_TIMEOUT_INTERVAL = 5000;
- Object.defineProperty(j$, 'DEFAULT_TIMEOUT_INTERVAL', {
- get: function() {
- return DEFAULT_TIMEOUT_INTERVAL;
- },
- set: function(newValue) {
- j$.private.util.validateTimeout(
- newValue,
- 'jasmine.DEFAULT_TIMEOUT_INTERVAL'
- );
- DEFAULT_TIMEOUT_INTERVAL = newValue;
- }
- });
-
- j$.getGlobal = function() {
- return jasmineGlobal;
- };
-
- /**
- * Get the currently booted Jasmine Environment.
- *
- * @name jasmine.getEnv
- * @since 1.3.0
- * @function
- * @return {Env}
- */
- Object.defineProperty(j$, 'getEnv', {
- enumerable: true,
- value: function(options) {
- const env = (j$.private.currentEnv_ =
- j$.private.currentEnv_ || new j$.private.Env(options));
- //jasmine. singletons in here (setTimeout blah blah).
- return env;
- }
- });
-
- j$.private.isObject = function(value) {
- return (
- value !== undefined && value !== null && j$.private.isA('Object', value)
- );
- };
-
- j$.private.isString = function(value) {
- return j$.private.isA('String', value);
- };
-
- j$.private.isNumber = function(value) {
- return j$.private.isA('Number', value);
- };
-
- j$.private.isFunction = function(value) {
- return j$.private.isA('Function', value);
- };
-
- j$.private.isAsyncFunction = function(value) {
- return j$.private.isA('AsyncFunction', value);
- };
-
- j$.private.isGeneratorFunction = function(value) {
- return j$.private.isA('GeneratorFunction', value);
- };
-
- j$.private.isTypedArray = function(value) {
- return (
- j$.private.isA('Float32Array', value) ||
- j$.private.isA('Float64Array', value) ||
- j$.private.isA('Int16Array', value) ||
- j$.private.isA('Int32Array', value) ||
- j$.private.isA('Int8Array', value) ||
- j$.private.isA('Uint16Array', value) ||
- j$.private.isA('Uint32Array', value) ||
- j$.private.isA('Uint8Array', value) ||
- j$.private.isA('Uint8ClampedArray', value)
- );
- };
-
- j$.private.isA = function(typeName, value) {
- return j$.private.getType(value) === '[object ' + typeName + ']';
- };
-
- j$.private.isError = function(value) {
- if (!value) {
- return false;
- }
-
- if (value instanceof Error) {
- return true;
- }
-
- return typeof value.stack === 'string' && typeof value.message === 'string';
- };
-
- j$.private.isAsymmetricEqualityTester = function(obj) {
- return obj ? j$.private.isA('Function', obj.asymmetricMatch) : false;
- };
-
- j$.private.getType = function(value) {
- return Object.prototype.toString.apply(value);
- };
-
- j$.private.isDomNode = function(obj) {
- // Node is a function, because constructors
- return typeof jasmineGlobal.Node !== 'undefined'
- ? obj instanceof jasmineGlobal.Node
- : obj !== null &&
- typeof obj === 'object' &&
- typeof obj.nodeType === 'number' &&
- typeof obj.nodeName === 'string';
- // return obj.nodeType > 0;
- };
-
- j$.private.isMap = function(obj) {
- return (
- obj !== null &&
- typeof obj !== 'undefined' &&
- obj.constructor === jasmineGlobal.Map
- );
- };
-
- j$.private.isSet = function(obj) {
- return (
- obj !== null &&
- typeof obj !== 'undefined' &&
- obj.constructor === jasmineGlobal.Set
- );
- };
-
- j$.private.isWeakMap = function(obj) {
- return (
- obj !== null &&
- typeof obj !== 'undefined' &&
- obj.constructor === jasmineGlobal.WeakMap
- );
- };
-
- j$.private.isURL = function(obj) {
- return (
- obj !== null &&
- typeof obj !== 'undefined' &&
- obj.constructor === jasmineGlobal.URL
- );
- };
-
- j$.private.isIterable = function(value) {
- return value && !!value[Symbol.iterator];
- };
-
- j$.private.isDataView = function(obj) {
- return (
- obj !== null &&
- typeof obj !== 'undefined' &&
- obj.constructor === jasmineGlobal.DataView
- );
- };
-
- j$.private.isPromise = function(obj) {
- return !!obj && obj.constructor === jasmineGlobal.Promise;
- };
-
- j$.private.isPromiseLike = function(obj) {
- return !!obj && j$.private.isFunction(obj.then);
- };
-
- j$.private.fnNameFor = function(func) {
- if (func.name) {
- return func.name;
- }
-
- const matches =
- func.toString().match(/^\s*function\s*(\w+)\s*\(/) ||
- func.toString().match(/^\s*\[object\s*(\w+)Constructor\]/);
-
- return matches ? matches[1] : '';
- };
-
- j$.private.isPending = function(promise) {
- const sentinel = {};
- return Promise.race([promise, Promise.resolve(sentinel)]).then(
- function(result) {
- return result === sentinel;
- },
- function() {
- return false;
- }
- );
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value being compared matches every provided equality tester.
- * @name asymmetricEqualityTesters.allOf
- * @emittedName jasmine.allOf
- * @since 5.13.0
- * @function
- * @param {...*} arguments - The asymmetric equality checkers to compare.
- */
- j$.allOf = function() {
- return new j$.AllOf(...arguments);
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value being compared is an instance of the specified class/constructor.
- * @name asymmetricEqualityTesters.any
- * @emittedName jasmine.any
- * @since 1.3.0
- * @function
- * @param {Constructor} clazz - The constructor to check against.
- */
- j$.any = function(clazz) {
- return new j$.private.Any(clazz);
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value being compared is not `null` and not `undefined`.
- * @name asymmetricEqualityTesters.anything
- * @emittedName jasmine.anything
- * @since 2.2.0
- * @function
- */
- j$.anything = function() {
- return new j$.private.Anything();
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value being compared is `true` or anything truthy.
- * @name asymmetricEqualityTesters.truthy
- * @emittedName jasmine.truthy
- * @since 3.1.0
- * @function
- */
- j$.truthy = function() {
- return new j$.private.Truthy();
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value being compared is `null`, `undefined`, `0`, `false` or anything
- * falsy.
- * @name asymmetricEqualityTesters.falsy
- * @emittedName jasmine.falsy
- * @since 3.1.0
- * @function
- */
- j$.falsy = function() {
- return new j$.private.Falsy();
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value being compared is empty.
- * @name asymmetricEqualityTesters.empty
- * @emittedName jasmine.empty
- * @since 3.1.0
- * @function
- */
- j$.empty = function() {
- return new j$.private.Empty();
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that passes if the actual value is
- * the same as the sample as determined by the `===` operator.
- * @name asymmetricEqualityTesters.is
- * @emittedName jasmine.is
- * @function
- * @param {Object} sample - The value to compare the actual to.
- */
- j$.is = function(sample) {
- return new j$.private.Is(sample);
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value being compared is not empty.
- * @name asymmetricEqualityTesters.notEmpty
- * @emittedName jasmine.notEmpty
- * @since 3.1.0
- * @function
- */
- j$.notEmpty = function() {
- return new j$.private.NotEmpty();
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value being compared contains at least the specified keys and values.
- * @name asymmetricEqualityTesters.objectContaining
- * @emittedName jasmine.objectContaining
- * @since 1.3.0
- * @function
- * @param {Object} sample - The subset of properties that _must_ be in the actual.
- */
- j$.objectContaining = function(sample) {
- return new j$.private.ObjectContaining(sample);
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value is a `String` that matches the `RegExp` or `String`.
- * @name asymmetricEqualityTesters.stringMatching
- * @emittedName jasmine.stringMatching
- * @since 2.2.0
- * @function
- * @param {RegExp|String} expected
- */
- j$.stringMatching = function(expected) {
- return new j$.private.StringMatching(expected);
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value is a `String` that contains the specified `String`.
- * @name asymmetricEqualityTesters.stringContaining
- * @emittedName jasmine.stringContaining
- * @since 3.10.0
- * @function
- * @param {String} expected
- */
- j$.stringContaining = function(expected) {
- return new j$.private.StringContaining(expected);
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value is an `Array` that contains at least the elements in the sample.
- * @name asymmetricEqualityTesters.arrayContaining
- * @emittedName jasmine.arrayContaining
- * @since 2.2.0
- * @function
- * @param {Array} sample
- */
- j$.arrayContaining = function(sample) {
- return new j$.private.ArrayContaining(sample);
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if the actual
- * value is an `Array` that contains all of the elements in the sample in
- * any order.
- * @name asymmetricEqualityTesters.arrayWithExactContents
- * @emittedName jasmine.arrayWithExactContents
- * @since 2.8.0
- * @function
- * @param {Array} sample
- */
- j$.arrayWithExactContents = function(sample) {
- return new j$.private.ArrayWithExactContents(sample);
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if every
- * key/value pair in the sample passes the deep equality comparison
- * with at least one key/value pair in the actual value being compared
- * @name asymmetricEqualityTesters.mapContaining
- * @emittedName jasmine.mapContaining
- * @since 3.5.0
- * @function
- * @param {Map} sample - The subset of items that _must_ be in the actual.
- */
- j$.mapContaining = function(sample) {
- return new j$.private.MapContaining(sample);
- };
-
- /**
- * Get an {@link AsymmetricEqualityTester} that will succeed if every item
- * in the sample passes the deep equality comparison
- * with at least one item in the actual value being compared
- * @name asymmetricEqualityTesters.setContaining
- * @emittedName jasmine.setContaining
- * @since 3.5.0
- * @function
- * @param {Set} sample - The subset of items that _must_ be in the actual.
- */
- j$.setContaining = function(sample) {
- return new j$.private.SetContaining(sample);
- };
-
- /**
- * Determines whether the provided function is a Jasmine spy.
- * @name jasmine.isSpy
- * @since 2.0.0
- * @function
- * @param {Function} putativeSpy - The function to check.
- * @return {Boolean}
- */
- j$.isSpy = function(putativeSpy) {
- if (!putativeSpy) {
- return false;
- }
- return (
- putativeSpy.and instanceof j$.private.SpyStrategy &&
- putativeSpy.calls instanceof j$.private.CallTracker
- );
- };
-
- /**
- * Logs a message for use in debugging. If the spec fails, trace messages
- * will be included in the {@link SpecDoneEvent|result} passed to the
- * reporter's specDone method.
- *
- * This method should be called only when a spec (including any associated
- * beforeEach or afterEach functions) is running.
- * @function
- * @name jasmine.debugLog
- * @since 4.0.0
- * @param {String} msg - The message to log
- */
- j$.debugLog = function(msg) {
- j$.getEnv().debugLog(msg);
- };
-
- /**
- * Replaces Jasmine's global error handling with a spy. This prevents Jasmine
- * from treating uncaught exceptions and unhandled promise rejections
- * as spec failures and allows them to be inspected using the spy's
- * {@link Spy#calls|calls property} and related matchers such as
- * {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}.
- *
- * After installing the spy, spyOnGlobalErrorsAsync immediately calls its
- * argument, which must be an async or promise-returning function. The spy
- * will be passed as the first argument to that callback. Normal error
- * handling will be restored when the promise returned from the callback is
- * settled.
- *
- * When the JavaScript runtime reports an uncaught error or unhandled rejection,
- * the spy will be called with a single parameter representing Jasmine's best
- * effort at describing the error. This parameter may be of any type, because
- * JavaScript allows anything to be thrown or used as the reason for a
- * rejected promise, but Error instances and strings are most common.
- *
- * Note: The JavaScript runtime may deliver uncaught error events and unhandled
- * rejection events asynchronously, especially in browsers. If the event
- * occurs after the promise returned from the callback is settled, it won't
- * be routed to the spy even if the underlying error occurred previously.
- * It's up to you to ensure that all of the error/rejection events that you
- * want to handle have occurred before you resolve the promise returned from
- * the callback.
- *
- * You must ensure that the `it`/`beforeEach`/etc fn that called
- * `spyOnGlobalErrorsAsync` does not signal completion until after the
- * promise returned by `spyOnGlobalErrorsAsync` is resolved. Normally this is
- * done by `await`ing the returned promise. Leaving the global error spy
- * installed after the `it`/`beforeEach`/etc fn that installed it signals
- * completion is likely to cause problems and is not supported.
- * @name jasmine.spyOnGlobalErrorsAsync
- * @function
- * @async
- * @param {AsyncFunction} fn - A function to run, during which the global error spy will be effective
- * @example
- * it('demonstrates global error spies', async function() {
- * await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
- * setTimeout(function() {
- * throw new Error('the expected error');
- * });
- * await new Promise(function(resolve) {
- * setTimeout(resolve);
- * });
- * const expected = new Error('the expected error');
- * expect(globalErrorSpy).toHaveBeenCalledWith(expected);
- * });
- * });
- */
- j$.spyOnGlobalErrorsAsync = async function(fn) {
- await jasmine.getEnv().spyOnGlobalErrorsAsync(fn);
- };
-};
-
-getJasmineRequireObj().util = function(j$) {
- 'use strict';
-
- const util = {};
-
- util.clone = function(obj) {
- if (Object.prototype.toString.apply(obj) === '[object Array]') {
- return obj.slice();
- }
-
- const cloned = {};
- for (const prop in obj) {
- if (obj.hasOwnProperty(prop)) {
- cloned[prop] = obj[prop];
- }
- }
-
- return cloned;
- };
-
- util.cloneArgs = function(args) {
- return Array.from(args).map(function(arg) {
- const str = Object.prototype.toString.apply(arg),
- primitives = /^\[object (Boolean|String|RegExp|Number)/;
-
- // All falsey values are either primitives, `null`, or `undefined.
- if (!arg || str.match(primitives)) {
- return arg;
- } else if (str === '[object Date]') {
- return new Date(arg.valueOf());
- } else {
- return j$.private.util.clone(arg);
- }
- });
- };
-
- util.getPropertyDescriptor = function(obj, methodName) {
- let descriptor,
- proto = obj;
-
- do {
- descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
- proto = Object.getPrototypeOf(proto);
- } while (!descriptor && proto);
-
- return descriptor;
- };
-
- util.has = function(obj, key) {
- return Object.prototype.hasOwnProperty.call(obj, key);
- };
-
- function callerFile() {
- const trace = new j$.private.StackTrace(new Error());
- return trace.frames[1].file;
- }
-
- util.jasmineFile = (function() {
- let result;
-
- return function() {
- if (!result) {
- result = callerFile();
- }
-
- return result;
- };
- })();
-
- util.validateTimeout = function(timeout, msgPrefix) {
- // Timeouts are implemented with setTimeout, which only supports a limited
- // range of values. The limit is unspecified, as is the behavior when it's
- // exceeded. But on all currently supported JS runtimes, setTimeout calls
- // the callback immediately when the timeout is greater than 2147483647
- // (the maximum value of a signed 32 bit integer).
- const max = 2147483647;
-
- if (timeout > max) {
- throw new Error(
- (msgPrefix || 'Timeout value') + ' cannot be greater than ' + max
- );
- }
- };
-
- util.assertReporterCloneable = function(v, msgPrefix) {
- try {
- // Reporter events are cloned internally via structuredClone, and it's
- // common for reporters (including jasmine-browser-runner's) to JSON
- // serialize them.
- JSON.stringify(structuredClone(v));
- } catch (e) {
- throw new Error(`${msgPrefix} can't be cloned`, { cause: e });
- }
- };
-
- return util;
-};
-
-getJasmineRequireObj().Spec = function(j$) {
- 'use strict';
-
- class Spec {
- #autoCleanClosures;
- #throwOnExpectationFailure;
- #timer;
- #metadata;
- #executionState;
-
- constructor(attrs) {
- this.expectationFactory = attrs.expectationFactory;
- this.asyncExpectationFactory = attrs.asyncExpectationFactory;
- this.id = attrs.id;
- this.filename = attrs.filename;
- this.parentSuiteId = attrs.parentSuiteId;
- this.description = attrs.description || '';
- this.queueableFn = attrs.queueableFn;
- this.beforeAndAfterFns =
- attrs.beforeAndAfterFns ||
- function() {
- return { befores: [], afters: [] };
- };
- this.userContext =
- attrs.userContext ||
- function() {
- return {};
- };
- this.getPath = function() {
- return attrs.getPath ? attrs.getPath(this) : [];
- };
-
- this.#autoCleanClosures =
- attrs.autoCleanClosures === undefined
- ? true
- : !!attrs.autoCleanClosures;
- this.onLateError = attrs.onLateError || function() {};
- this.#throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
- this.#timer = attrs.timer || new j$.Timer();
-
- this.reset();
-
- if (!this.queueableFn.fn) {
- this.exclude();
- }
- }
-
- addExpectationResult(passed, data, isError) {
- const expectationResult = j$.private.buildExpectationResult(data);
-
- if (passed) {
- this.#executionState.passedExpectations.push(expectationResult);
- } else {
- if (this.reportedDone) {
- this.onLateError(expectationResult);
- } else {
- this.#executionState.failedExpectations.push(expectationResult);
- }
-
- if (this.#throwOnExpectationFailure && !isError) {
- throw new j$.private.errors.ExpectationFailed();
- }
- }
- }
-
- getSpecProperty(key) {
- this.#executionState.properties = this.#executionState.properties || {};
- return this.#executionState.properties[key];
- }
-
- setSpecProperty(key, value) {
- // Key and value will eventually be cloned during reporting. The error
- // thrown at that point if they aren't cloneable isn't very helpful.
- // Throw a better one now.
- if (!j$.private.isString(key)) {
- throw new Error('Key must be a string');
- }
- j$.private.util.assertReporterCloneable(value, 'Value');
-
- this.#executionState.properties = this.#executionState.properties || {};
- this.#executionState.properties[key] = value;
- }
-
- executionStarted() {
- this.#timer.start();
- }
-
- executionFinished(excluded, failSpecWithNoExp) {
- this.#executionState.dynamicallyExcluded = excluded;
- this.#executionState.requireExpectations = failSpecWithNoExp;
-
- if (this.#autoCleanClosures) {
- this.queueableFn.fn = null;
- }
-
- this.#executionState.duration = this.#timer.elapsed();
-
- if (this.status() !== 'failed') {
- this.#executionState.debugLogs = null;
- }
- }
-
- hadBeforeAllFailure() {
- this.addExpectationResult(
- false,
- {
- passed: false,
- message:
- 'Not run because a beforeAll function failed. The ' +
- 'beforeAll failure will be reported on the suite that ' +
- 'caused it.'
- },
- true
- );
- }
-
- reset() {
- this.#executionState = {
- failedExpectations: [],
- passedExpectations: [],
- deprecationWarnings: [],
- pendingReason: this.excludeMessage || '',
- duration: null,
- properties: null,
- debugLogs: null,
- // TODO: better naming. Don't make 'excluded' mean two things.
- dynamicallyExcluded: false,
- requireExpectations: false,
- markedPending: this.markedExcluding
- };
- this.reportedDone = false;
- }
-
- startedEvent() {
- /**
- * @typedef SpecStartedEvent
- * @property {String} id - The unique id of this spec.
- * @property {String} description - The description passed to the {@link it} that created this spec.
- * @property {String} fullName - The full description including all ancestors of this spec.
- * @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
- * @property {String} filename - Deprecated. The name of the file the spec was defined in.
- * Note: The value may be incorrect if zone.js is installed or
- * `it`/`fit`/`xit` have been replaced with versions that don't maintain the
- * same call stack height as the originals. This property may be removed in
- * a future version unless there is enough user interest in keeping it.
- * See {@link https://github.com/jasmine/jasmine/issues/2065}.
- * @since 2.0.0
- */
- return this.#commonEventFields();
- }
-
- doneEvent() {
- /**
- * @typedef SpecDoneEvent
- * @property {String} id - The unique id of this spec.
- * @property {String} description - The description passed to the {@link it} that created this spec.
- * @property {String} fullName - The full description including all ancestors of this spec.
- * @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
- * @property {String} filename - The name of the file the spec was defined in.
- * Note: The value may be incorrect if zone.js is installed or
- * `it`/`fit`/`xit` have been replaced with versions that don't maintain the
- * same call stack height as the originals. You can fix that by setting
- * {@link Configuration#extraItStackFrames}.
- * @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
- * @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
- * @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
- * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
- * @property {String} status - The result of this spec. May be 'passed', 'failed', 'pending', or 'excluded'.
- * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
- * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
- * @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec.
- * @since 2.0.0
- */
- const event = {
- ...this.#commonEventFields(),
- status: this.status()
- };
- const toCopy = [
- 'failedExpectations',
- 'passedExpectations',
- 'deprecationWarnings',
- 'pendingReason',
- 'duration',
- 'properties',
- 'debugLogs'
- ];
-
- for (const k of toCopy) {
- event[k] = this.#executionState[k];
- }
-
- return event;
- }
-
- #commonEventFields() {
- return {
- id: this.id,
- description: this.description,
- fullName: this.getFullName(),
- parentSuiteId: this.parentSuiteId,
- filename: this.filename
- };
- }
-
- handleException(e) {
- if (Spec.isPendingSpecException(e)) {
- this.pend(extractCustomPendingMessage(e));
- return;
- }
-
- if (e instanceof j$.private.errors.ExpectationFailed) {
- return;
- }
-
- this.addExpectationResult(
- false,
- {
- matcherName: '',
- passed: false,
- error: e
- },
- true
- );
- }
-
- pend(message) {
- this.#executionState.markedPending = true;
- if (message) {
- this.#executionState.pendingReason = message;
- }
- }
-
- get markedPending() {
- return this.#executionState.markedPending;
- }
-
- // Like pend(), but pending state will survive reset().
- // Useful for fit, xit, where pending state remains.
- exclude(message) {
- this.markedExcluding = true;
- if (this.message) {
- this.excludeMessage = message;
- }
- this.pend(message);
- }
-
- status() {
- if (this.#executionState.dynamicallyExcluded) {
- return 'excluded';
- }
-
- if (this.markedPending) {
- return 'pending';
- }
-
- if (
- this.#executionState.failedExpectations.length > 0 ||
- (this.#executionState.requireExpectations &&
- this.#executionState.failedExpectations.length +
- this.#executionState.passedExpectations.length ===
- 0)
- ) {
- return 'failed';
- }
-
- return 'passed';
- }
-
- getFullName() {
- return this.getPath().join(' ');
- }
-
- addDeprecationWarning(deprecation) {
- if (typeof deprecation === 'string') {
- deprecation = { message: deprecation };
- }
- this.#executionState.deprecationWarnings.push(
- j$.private.buildExpectationResult(deprecation)
- );
- }
-
- debugLog(msg) {
- if (!this.#executionState.debugLogs) {
- this.#executionState.debugLogs = [];
- }
-
- /**
- * @typedef DebugLogEntry
- * @property {String} message - The message that was passed to {@link jasmine.debugLog}.
- * @property {number} timestamp - The time when the entry was added, in
- * milliseconds from the spec's start time
- */
- this.#executionState.debugLogs.push({
- message: msg,
- timestamp: this.#timer.elapsed()
- });
- }
-
- /**
- * @interface Spec
- * @see Configuration#specFilter
- * @since 2.0.0
- */
- get metadata() {
- // NOTE: Although most of jasmine-core only exposes these metadata objects,
- // actual Spec instances are still passed to Configuration#specFilter. Until
- // that is fixed, it's important to make sure that all metadata properties
- // also exist in compatible form on the underlying Spec.
- if (!this.#metadata) {
- this.#metadata = {
- /**
- * The unique ID of this spec.
- * @name Spec#id
- * @readonly
- * @type {string}
- * @since 2.0.0
- */
- id: this.id,
-
- /**
- * The description passed to the {@link it} that created this spec.
- * @name Spec#description
- * @readonly
- * @type {string}
- * @since 2.0.0
- */
- description: this.description,
-
- /**
- * The full description including all ancestors of this spec.
- * @name Spec#getFullName
- * @function
- * @returns {string}
- * @since 2.0.0
- */
- getFullName: this.getFullName.bind(this),
-
- /**
- * The full path of the spec, as an array of names.
- * @name Spec#getPath
- * @function
- * @returns {Array.}
- * @since 5.7.0
- */
- getPath: this.getPath.bind(this),
-
- /**
- * The name of the file the spec was defined in.
- * Note: The value may be incorrect if zone.js is installed or
- * `it`/`fit`/`xit` have been replaced with versions that don't maintain the
- * same call stack height as the originals. You can fix that by setting
- * {@link Configuration#extraItStackFrames}.
- * @name Spec#filename
- * @readonly
- * @type {string}
- * @since 5.13.0
- */
- filename: this.filename
- };
- }
-
- return this.#metadata;
- }
- }
-
- const extractCustomPendingMessage = function(e) {
- const fullMessage = e.toString(),
- boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
- boilerplateEnd =
- boilerplateStart + Spec.pendingSpecExceptionMessage.length;
-
- return fullMessage.slice(boilerplateEnd);
- };
-
- Spec.pendingSpecExceptionMessage = '=> marked Pending';
-
- Spec.isPendingSpecException = function(e) {
- return !!(
- e &&
- e.toString &&
- e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1
- );
- };
-
- return Spec;
-};
-
-getJasmineRequireObj().Order = function() {
- 'use strict';
-
- function Order(options) {
- this.random = 'random' in options ? options.random : true;
- const seed = (this.seed = options.seed || generateSeed());
- this.sort = this.random ? randomOrder : naturalOrder;
-
- function naturalOrder(items) {
- return items;
- }
-
- function randomOrder(items) {
- const copy = items.slice();
- copy.sort(function(a, b) {
- return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id);
- });
- return copy;
- }
-
- function generateSeed() {
- return String(Math.random()).slice(-5);
- }
-
- // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function
- // used to get a different output when the key changes slightly.
- // We use your return to sort the children randomly in a consistent way when
- // used in conjunction with a seed
-
- function jenkinsHash(key) {
- let hash, i;
- for (hash = i = 0; i < key.length; ++i) {
- hash += key.charCodeAt(i);
- hash += hash << 10;
- hash ^= hash >> 6;
- }
- hash += hash << 3;
- hash ^= hash >> 11;
- hash += hash << 15;
- return hash;
- }
- }
-
- return Order;
-};
-
-getJasmineRequireObj().Env = function(j$) {
- 'use strict';
-
- const DEFAULT_IT_DESCRIBE_STACK_DEPTH = 3;
-
- /**
- * @class Env
- * @since 2.0.0
- * @classdesc The Jasmine environment.
- * _Note:_ Do not construct this directly. You can obtain the Env instance by
- * calling {@link jasmine.getEnv}.
- * @hideconstructor
- */
- function Env(envOptions) {
- envOptions = envOptions || {};
-
- const self = this;
- const GlobalErrors = envOptions.GlobalErrors || j$.private.GlobalErrors;
- const global = envOptions.global || j$.getGlobal();
-
- const realSetTimeout = global.setTimeout;
- const realClearTimeout = global.clearTimeout;
- const stackClearer = j$.private.getStackClearer(global);
- this.clock = new j$.private.Clock(
- global,
- function() {
- return new j$.private.DelayedFunctionScheduler();
- },
- new j$.private.MockDate(global)
- );
-
- const globalErrors = new GlobalErrors(
- global,
- // Configuration is late-bound because GlobalErrors needs to be constructed
- // before it's set to detect load-time errors in browsers
- () => this.configuration()
- );
- const { installGlobalErrors, uninstallGlobalErrors } = (function() {
- let installed = false;
-
- return {
- installGlobalErrors() {
- if (!installed) {
- globalErrors.install();
- installed = true;
- }
- },
- uninstallGlobalErrors() {
- if (installed) {
- globalErrors.uninstall();
- installed = false;
- }
- }
- };
- })();
-
- const runableResources = new j$.private.RunableResources({
- getCurrentRunableId: function() {
- const r = runner.currentRunable();
- return r ? r.id : null;
- },
- globalErrors
- });
-
- let reportDispatcher;
- let topSuite;
- let runner;
- let parallelLoadingState = null; // 'specs', 'helpers', or null for non-parallel
-
- const config = new j$.private.Configuration();
-
- if (!envOptions.suppressLoadErrors) {
- installGlobalErrors();
- globalErrors.pushListener(function loadtimeErrorHandler(error) {
- topSuite.addExpectationResult(false, {
- passed: false,
- globalErrorType: 'load',
- message: error.message,
- stack: error.stack,
- filename: error.filename,
- lineno: error.lineno
- });
- });
- }
-
- /**
- * Configure your jasmine environment
- * @name Env#configure
- * @since 3.3.0
- * @argument {Configuration} configuration
- * @function
- */
- this.configure = function(changes) {
- if (parallelLoadingState) {
- throw new Error(
- 'Jasmine cannot be configured via Env in parallel mode'
- );
- }
-
- config.update(changes);
- deprecator.verboseDeprecations(config.verboseDeprecations);
- stackClearer.setSafariYieldStrategy(config.safariYieldStrategy);
- };
-
- /**
- * Get the current configuration for your jasmine environment
- * @name Env#configuration
- * @since 3.3.0
- * @function
- * @returns {Configuration}
- */
- this.configuration = function() {
- return config.copy();
- };
-
- this.setDefaultSpyStrategy = function(defaultStrategyFn) {
- runableResources.setDefaultSpyStrategy(defaultStrategyFn);
- };
-
- this.addSpyStrategy = function(name, fn) {
- runableResources.customSpyStrategies()[name] = fn;
- };
-
- this.addCustomEqualityTester = function(tester) {
- runableResources.customEqualityTesters().push(tester);
- };
-
- this.addMatchers = function(matchersToAdd) {
- runableResources.addCustomMatchers(matchersToAdd);
- };
-
- this.addAsyncMatchers = function(matchersToAdd) {
- runableResources.addCustomAsyncMatchers(matchersToAdd);
- };
-
- this.addCustomObjectFormatter = function(formatter) {
- runableResources.customObjectFormatters().push(formatter);
- };
-
- j$.private.Expectation.addCoreMatchers(j$.private.matchers);
- j$.private.Expectation.addAsyncCoreMatchers(j$.private.asyncMatchers);
-
- const expectationFactory = function(actual, spec) {
- return j$.private.Expectation.factory({
- matchersUtil: runableResources.makeMatchersUtil(),
- customMatchers: runableResources.customMatchers(),
- actual: actual,
- addExpectationResult: addExpectationResult
- });
-
- function addExpectationResult(passed, result) {
- return spec.addExpectationResult(passed, result);
- }
- };
-
- const handleThrowUnlessFailure = function(passed, result) {
- if (!passed) {
- /**
- * @interface
- * @name ThrowUnlessFailure
- * @extends Error
- * @description Represents a failure of an expectation evaluated with
- * {@link throwUnless}. Properties of this error are a subset of the
- * properties of {@link ExpectationResult} and have the same values.
- *
- * @property {String} matcherName - The name of the matcher that was executed for this expectation.
- * @property {String} message - The failure message for the expectation.
- */
- const error = new Error(result.message);
- error.message = result.message;
- error.matcherName = result.matcherName;
- throw error;
- }
- };
-
- const throwUnlessFactory = function(actual, spec) {
- return j$.private.Expectation.factory({
- matchersUtil: runableResources.makeMatchersUtil(),
- customMatchers: runableResources.customMatchers(),
- actual: actual,
- addExpectationResult: handleThrowUnlessFailure
- });
- };
-
- const throwUnlessAsyncFactory = function(actual, spec) {
- return j$.private.Expectation.asyncFactory({
- matchersUtil: runableResources.makeMatchersUtil(),
- customAsyncMatchers: runableResources.customAsyncMatchers(),
- actual: actual,
- addExpectationResult: handleThrowUnlessFailure
- });
- };
-
- // TODO: Unify recordLateError with recordLateExpectation? The extra
- // diagnostic info added by the latter is probably useful in most cases.
- function recordLateError(error) {
- const isExpectationResult =
- error.matcherName !== undefined && error.passed !== undefined;
- const result = isExpectationResult
- ? error
- : j$.private.buildExpectationResult({
- error,
- passed: false,
- matcherName: '',
- expected: '',
- actual: ''
- });
- routeLateFailure(result);
- }
-
- function recordLateExpectation(runable, runableType, result) {
- const delayedExpectationResult = {};
- Object.keys(result).forEach(function(k) {
- delayedExpectationResult[k] = result[k];
- });
- delayedExpectationResult.passed = false;
- delayedExpectationResult.globalErrorType = 'lateExpectation';
- delayedExpectationResult.message =
- runableType +
- ' "' +
- runable.getFullName() +
- '" ran a "' +
- result.matcherName +
- '" expectation after it finished.\n';
-
- if (result.message) {
- delayedExpectationResult.message +=
- 'Message: "' + result.message + '"\n';
- }
-
- delayedExpectationResult.message +=
- '1. Did you forget to return or await the result of expectAsync?\n' +
- '2. Was done() invoked before an async operation completed?\n' +
- '3. Did an expectation follow a call to done()?';
-
- topSuite.addExpectationResult(false, delayedExpectationResult);
- }
-
- function routeLateFailure(expectationResult) {
- // Report the result on the nearest ancestor suite that hasn't already
- // been reported done.
- for (let r = runner.currentRunable(); r; r = r.parentSuite) {
- if (!r.reportedDone) {
- if (r === topSuite) {
- expectationResult.globalErrorType = 'lateError';
- }
-
- r.addExpectationResult(false, expectationResult);
- return;
- }
- }
-
- // If we get here, all results have been reported and there's nothing we
- // can do except log the result and hope the user sees it.
- // eslint-disable-next-line no-console
- console.error('Jasmine received a result after the suite finished:');
- // eslint-disable-next-line no-console
- console.error(expectationResult);
- }
-
- const asyncExpectationFactory = function(actual, spec, runableType) {
- return j$.private.Expectation.asyncFactory({
- matchersUtil: runableResources.makeMatchersUtil(),
- customAsyncMatchers: runableResources.customAsyncMatchers(),
- actual: actual,
- addExpectationResult: addExpectationResult
- });
-
- function addExpectationResult(passed, result) {
- if (runner.currentRunable() !== spec) {
- recordLateExpectation(spec, runableType, result);
- }
- return spec.addExpectationResult(passed, result);
- }
- };
-
- /**
- * Causes a deprecation warning to be logged to the console and reported to
- * reporters.
- *
- * The optional second parameter is an object that can have either of the
- * following properties:
- *
- * omitStackTrace: Whether to omit the stack trace. Optional. Defaults to
- * false. This option is ignored if the deprecation is an Error. Set this
- * when the stack trace will not contain anything that helps the user find
- * the source of the deprecation.
- *
- * ignoreRunnable: Whether to log the deprecation on the root suite, ignoring
- * the spec or suite that's running when it happens. Optional. Defaults to
- * false.
- *
- * @name Env#deprecated
- * @since 2.99
- * @function
- * @param {String|Error} deprecation The deprecation message
- * @param {Object} [options] Optional extra options, as described above
- */
- Object.defineProperty(this, 'deprecated', {
- enumerable: true,
- value: function(deprecation, options) {
- const runable = runner.currentRunable() || topSuite;
- deprecator.addDeprecationWarning(runable, deprecation, options);
- }
- });
-
- function runQueue(options) {
- options.clearStack = options.clearStack || stackClearer;
- options.timeout = {
- setTimeout: realSetTimeout,
- clearTimeout: realClearTimeout
- };
- options.fail = self.fail;
- options.globalErrors = globalErrors;
- options.onException =
- options.onException ||
- function(e) {
- (runner.currentRunable() || topSuite).handleException(e);
- };
-
- new j$.private.QueueRunner(options).execute();
- }
-
- const suiteBuilder = new j$.private.SuiteBuilder({
- env: this,
- expectationFactory,
- asyncExpectationFactory,
- onLateError: recordLateError,
- runQueue
- });
- topSuite = suiteBuilder.topSuite;
- const deprecator =
- envOptions?.deprecator ?? new j$.private.Deprecator(topSuite);
-
- /**
- * Provides the root suite, through which all suites and specs can be
- * accessed.
- * @function
- * @name Env#topSuite
- * @return {Suite} the root suite
- * @since 2.0.0
- */
- this.topSuite = function() {
- ensureNonParallel('topSuite');
- return topSuite.metadata;
- };
-
- /**
- * This represents the available reporter callback for an object passed to {@link Env#addReporter}.
- * @interface Reporter
- * @see custom_reporter
- */
- reportDispatcher = new j$.private.ReportDispatcher(
- j$.private.reporterEvents,
- function(options) {
- options.SkipPolicy = j$.private.NeverSkipPolicy;
- return runQueue(options);
- },
- recordLateError
- );
-
- runner = new j$.private.Runner({
- topSuite,
- totalSpecsDefined: () => suiteBuilder.totalSpecsDefined,
- focusedRunables: () => suiteBuilder.focusedRunables,
- runableResources,
- reportDispatcher,
- runQueue,
- TreeProcessor: j$.private.TreeProcessor,
- globalErrors,
- getConfig: () => config
- });
-
- this.setParallelLoadingState = function(state) {
- parallelLoadingState = state;
- };
-
- this.parallelReset = function() {
- suiteBuilder.parallelReset();
- runner.parallelReset();
- };
-
- /**
- * Executes the specs.
- *
- * If called with no parameter or with a falsy parameter,
- * all specs will be executed except those that are excluded by a
- * [spec filter]{@link Configuration#specFilter} or other mechanism. If the
- * parameter is a list of spec/suite IDs, only those specs/suites will
- * be run.
- *
- * execute should not be called more than once unless the env has been
- * configured with `{autoCleanClosures: false}`.
- *
- * execute returns a promise. The promise will be resolved to the same
- * {@link JasmineDoneInfo|overall result} that's passed to a reporter's
- * `jasmineDone` method, even if the suite did not pass. To determine
- * whether the suite passed, check the value that the promise resolves to
- * or use a {@link Reporter}. The promise will be rejected in the case of
- * certain serious errors that prevent execution from starting.
- *
- * @name Env#execute
- * @since 2.0.0
- * @function
- * @async
- * @param {(string[])=} runablesToRun IDs of suites and/or specs to run
- * @return {Promise}
- */
- this.execute = async function(runablesToRun) {
- installGlobalErrors();
-
- // Karma incorrectly loads jasmine-core as an ES module. It isn't one,
- // and we don't test that configuration. Warn about it.
- if (j$.private.loadedAsBrowserEsm) {
- this.deprecated(
- "jasmine-core isn't an ES module but it was loaded as one. This is not a supported configuration."
- );
- }
-
- if (parallelLoadingState) {
- validateConfigForParallel();
- }
-
- const result = await runner.execute(runablesToRun);
- this.cleanup_();
- return result;
- };
-
- /**
- * Add a custom reporter to the Jasmine environment.
- * @name Env#addReporter
- * @since 2.0.0
- * @function
- * @param {Reporter} reporterToAdd The reporter to be added.
- * @see custom_reporter
- */
- this.addReporter = function(reporterToAdd) {
- if (parallelLoadingState) {
- throw new Error('Reporters cannot be added via Env in parallel mode');
- }
-
- reportDispatcher.addReporter(reporterToAdd);
- };
-
- /**
- * Provide a fallback reporter if no other reporters have been specified.
- * @name Env#provideFallbackReporter
- * @since 2.5.0
- * @function
- * @param {Reporter} reporterToAdd The reporter
- * @see custom_reporter
- */
- this.provideFallbackReporter = function(reporterToAdd) {
- reportDispatcher.provideFallbackReporter(reporterToAdd);
- };
-
- /**
- * Clear all registered reporters
- * @name Env#clearReporters
- * @since 2.5.2
- * @function
- */
- this.clearReporters = function() {
- if (parallelLoadingState) {
- throw new Error('Reporters cannot be removed via Env in parallel mode');
- }
-
- reportDispatcher.clearReporters();
- };
-
- /**
- * Configures whether Jasmine should allow the same function to be spied on
- * more than once during the execution of a spec. By default, spying on
- * a function that is already a spy will cause an error.
- * @name Env#allowRespy
- * @function
- * @since 2.5.0
- * @param {boolean} allow Whether to allow respying
- */
- this.allowRespy = function(allow) {
- runableResources.spyRegistry.allowRespy(allow);
- };
-
- this.spyOn = function() {
- return runableResources.spyRegistry.spyOn.apply(
- runableResources.spyRegistry,
- arguments
- );
- };
-
- this.spyOnProperty = function() {
- return runableResources.spyRegistry.spyOnProperty.apply(
- runableResources.spyRegistry,
- arguments
- );
- };
-
- this.spyOnAllFunctions = function() {
- return runableResources.spyRegistry.spyOnAllFunctions.apply(
- runableResources.spyRegistry,
- arguments
- );
- };
-
- this.createSpy = function(name, originalFn) {
- return runableResources.spyFactory.createSpy(name, originalFn);
- };
-
- this.createSpyObj = function(baseName, methodNames, propertyNames) {
- return runableResources.spyFactory.createSpyObj(
- baseName,
- methodNames,
- propertyNames
- );
- };
-
- this.spyOnGlobalErrorsAsync = async function(fn) {
- const spy = this.createSpy('global error handler');
- const associatedRunable = runner.currentRunable();
- let cleanedUp = false;
-
- globalErrors.setOverrideListener(spy, () => {
- if (!cleanedUp) {
- const message =
- 'Global error spy was not uninstalled. (Did you ' +
- 'forget to await the return value of spyOnGlobalErrorsAsync?)';
- associatedRunable.addExpectationResult(false, {
- matcherName: '',
- passed: false,
- expected: '',
- actual: '',
- message,
- error: null
- });
- }
-
- cleanedUp = true;
- });
-
- try {
- const maybePromise = fn(spy);
-
- if (!j$.private.isPromiseLike(maybePromise)) {
- throw new Error(
- 'The callback to spyOnGlobalErrorsAsync must be an async or promise-returning function'
- );
- }
-
- await maybePromise;
- } finally {
- if (!cleanedUp) {
- cleanedUp = true;
- globalErrors.removeOverrideListener();
- }
- }
- };
-
- function ensureIsNotNested(method) {
- const runable = runner.currentRunable();
- if (runable !== null && runable !== undefined) {
- throw new Error(
- "'" + method + "' should only be used in 'describe' function"
- );
- }
- }
-
- function ensureNonParallel(method) {
- if (parallelLoadingState) {
- throw new Error(`'${method}' is not available in parallel mode`);
- }
- }
-
- function ensureNonParallelOrInDescribe(msg) {
- if (parallelLoadingState && !suiteBuilder.inDescribe()) {
- throw new Error(msg);
- }
- }
-
- function ensureNonParallelOrInHelperOrInDescribe(method) {
- if (parallelLoadingState === 'specs' && !suiteBuilder.inDescribe()) {
- throw new Error(
- 'In parallel mode, ' +
- method +
- ' must be in a describe block or in a helper file'
- );
- }
- }
-
- function validateConfigForParallel() {
- if (!config.random) {
- throw new Error('Randomization cannot be disabled in parallel mode');
- }
-
- if (config.seed !== null && config.seed !== undefined) {
- throw new Error('Random seed cannot be set in parallel mode');
- }
- }
-
- this.describe = function(description, definitionFn) {
- ensureIsNotNested('describe');
- const filename = indirectCallerFilename(describeStackDepth());
- return suiteBuilder.describe(description, definitionFn, filename)
- .metadata;
- };
-
- this.xdescribe = function(description, definitionFn) {
- ensureIsNotNested('xdescribe');
- const filename = indirectCallerFilename(describeStackDepth());
- return suiteBuilder.xdescribe(description, definitionFn, filename)
- .metadata;
- };
-
- this.fdescribe = function(description, definitionFn) {
- ensureIsNotNested('fdescribe');
- ensureNonParallel('fdescribe');
- const filename = indirectCallerFilename(describeStackDepth());
- return suiteBuilder.fdescribe(description, definitionFn, filename)
- .metadata;
- };
-
- this.it = function(description, fn, timeout) {
- ensureIsNotNested('it');
- const filename = indirectCallerFilename(itStackDepth());
- return suiteBuilder.it(description, fn, timeout, filename).metadata;
- };
-
- this.xit = function(description, fn, timeout) {
- ensureIsNotNested('xit');
- const filename = indirectCallerFilename(itStackDepth());
- return suiteBuilder.xit(description, fn, timeout, filename).metadata;
- };
-
- this.fit = function(description, fn, timeout) {
- ensureIsNotNested('fit');
- ensureNonParallel('fit');
- const filename = indirectCallerFilename(itStackDepth());
- return suiteBuilder.fit(description, fn, timeout, filename).metadata;
- };
-
- function itStackDepth() {
- return DEFAULT_IT_DESCRIBE_STACK_DEPTH + config.extraItStackFrames;
- }
-
- function describeStackDepth() {
- return DEFAULT_IT_DESCRIBE_STACK_DEPTH + config.extraDescribeStackFrames;
- }
-
- /**
- * Get a user-defined property as part of the properties field of {@link SpecDoneEvent}
- * @name Env#getSpecProperty
- * @since 5.10.0
- * @function
- * @param {String} key The name of the property
- * @returns {*} The value of the property
- */
- this.getSpecProperty = function(key) {
- if (
- !runner.currentRunable() ||
- runner.currentRunable() == runner.currentSuite()
- ) {
- throw new Error(
- "'getSpecProperty' was used when there was no current spec"
- );
- }
- return runner.currentRunable().getSpecProperty(key);
- };
-
- /**
- * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult}
- * @name Env#setSpecProperty
- * @since 3.6.0
- * @function
- * @param {String} key The name of the property
- * @param {*} value The value of the property
- */
- this.setSpecProperty = function(key, value) {
- if (
- !runner.currentRunable() ||
- runner.currentRunable() == runner.currentSuite()
- ) {
- throw new Error(
- "'setSpecProperty' was used when there was no current spec"
- );
- }
- runner.currentRunable().setSpecProperty(key, value);
- };
-
- /**
- * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult}
- * @name Env#setSuiteProperty
- * @since 3.6.0
- * @function
- * @param {String} key The name of the property
- * @param {*} value The value of the property
- */
- this.setSuiteProperty = function(key, value) {
- if (!runner.currentSuite()) {
- throw new Error(
- "'setSuiteProperty' was used when there was no current suite"
- );
- }
- runner.currentSuite().setSuiteProperty(key, value);
- };
-
- this.debugLog = function(msg) {
- const maybeSpec = runner.currentRunable();
-
- if (!maybeSpec || !maybeSpec.debugLog) {
- throw new Error("'debugLog' was called when there was no current spec");
- }
-
- maybeSpec.debugLog(msg);
- };
-
- this.expect = function(actual) {
- const runable = runner.currentRunable();
-
- if (!runable) {
- throw new Error(
- "'expect' was used when there was no current spec, this could be because an asynchronous test timed out"
- );
- }
-
- return runable.expectationFactory(actual, runable);
- };
-
- this.expectAsync = function(actual) {
- const runable = runner.currentRunable();
-
- if (!runable) {
- throw new Error(
- "'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out"
- );
- }
-
- return runable.asyncExpectationFactory(actual, runable);
- };
-
- this.throwUnless = function(actual) {
- const runable = runner.currentRunable();
- return throwUnlessFactory(actual, runable);
- };
-
- this.throwUnlessAsync = function(actual) {
- const runable = runner.currentRunable();
- return throwUnlessAsyncFactory(actual, runable);
- };
-
- this.beforeEach = function(beforeEachFunction, timeout) {
- ensureIsNotNested('beforeEach');
- ensureNonParallelOrInHelperOrInDescribe('beforeEach');
- suiteBuilder.beforeEach(beforeEachFunction, timeout);
- };
-
- this.beforeAll = function(beforeAllFunction, timeout) {
- ensureIsNotNested('beforeAll');
- // This message is -npm-specific, but currently parallel operation is
- // only supported via -npm.
- ensureNonParallelOrInDescribe(
- "In parallel mode, 'beforeAll' " +
- 'must be in a describe block. Use the globalSetup config ' +
- 'property for exactly-once setup in parallel mode.'
- );
- suiteBuilder.beforeAll(beforeAllFunction, timeout);
- };
-
- this.afterEach = function(afterEachFunction, timeout) {
- ensureIsNotNested('afterEach');
- ensureNonParallelOrInHelperOrInDescribe('afterEach');
- suiteBuilder.afterEach(afterEachFunction, timeout);
- };
-
- this.afterAll = function(afterAllFunction, timeout) {
- ensureIsNotNested('afterAll');
- // This message is -npm-specific, but currently parallel operation is
- // only supported via -npm.
- ensureNonParallelOrInDescribe(
- "In parallel mode, 'afterAll' " +
- 'must be in a describe block. Use the globalTeardown config ' +
- 'property for exactly-once teardown in parallel mode.'
- );
- suiteBuilder.afterAll(afterAllFunction, timeout);
- };
-
- this.pending = function(message) {
- let fullMessage = j$.private.Spec.pendingSpecExceptionMessage;
- if (message) {
- fullMessage += message;
- }
- throw fullMessage;
- };
-
- this.fail = function(error) {
- if (!runner.currentRunable()) {
- throw new Error(
- "'fail' was used when there was no current spec, this could be because an asynchronous test timed out"
- );
- }
-
- let message = 'Failed';
- if (error) {
- message += ': ';
- if (error.message) {
- message += error.message;
- } else if (j$.private.isString(error)) {
- message += error;
- } else {
- // pretty print all kind of objects. This includes arrays.
- const pp = runableResources.makePrettyPrinter();
- message += pp(error);
- }
- }
-
- runner.currentRunable().addExpectationResult(false, {
- matcherName: '',
- passed: false,
- expected: '',
- actual: '',
- message: message,
- error: error && error.message ? error : null
- });
-
- if (config.stopSpecOnExpectationFailure) {
- throw new Error(message);
- }
- };
-
- this.pp = function(value) {
- const pp = runner.currentRunable()
- ? runableResources.makePrettyPrinter()
- : j$.private.basicPrettyPrinter;
- return pp(value);
- };
-
- this.cleanup_ = function() {
- uninstallGlobalErrors();
- };
-
- j$.private.deprecateMonkeyPatching(this, ['deprecated']);
- }
-
- function indirectCallerFilename(depth) {
- const frames = new j$.private.StackTrace(new Error()).frames;
- // The specified frame should always exist except in Jasmine's own tests,
- // which bypass the global it/describe layer, but could be absent in case
- // of misconfiguration. Don't crash if it's absent.
- return frames[depth] && frames[depth].file;
- }
-
- return Env;
-};
-
-getJasmineRequireObj().JsApiReporter = function(j$) {
- 'use strict';
-
- // TODO: remove in 7.0.
- /**
- * @name jsApiReporter
- * @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object.
- * @class
- * @hideconstructor
- * @deprecated In most cases jsApiReporter can simply be removed. If necessary, it can be replaced with a {@link Reporter|custom reporter}.
- */
- function JsApiReporter(options) {
- const timer = options.timer || new j$.Timer();
- let status = 'loaded';
-
- this.started = false;
- this.finished = false;
- this.runDetails = {};
-
- this.jasmineStarted = function() {
- this.started = true;
- status = 'started';
- timer.start();
- };
-
- let executionTime;
-
- this.jasmineDone = function(runDetails) {
- this.finished = true;
- this.runDetails = runDetails;
- executionTime = timer.elapsed();
- status = 'done';
- };
-
- /**
- * Get the current status for the Jasmine environment.
- * @name jsApiReporter#status
- * @since 2.0.0
- * @function
- * @return {String} - One of `loaded`, `started`, or `done`
- */
- this.status = function() {
- return status;
- };
-
- const suites = [],
- suites_hash = {};
-
- this.suiteStarted = function(result) {
- suites_hash[result.id] = result;
- };
-
- this.suiteDone = function(result) {
- storeSuite(result);
- };
-
- /**
- * Get the results for a set of suites.
- *
- * Retrievable in slices for easier serialization.
- * @name jsApiReporter#suiteResults
- * @since 2.1.0
- * @function
- * @param {Number} index - The position in the suites list to start from.
- * @param {Number} length - Maximum number of suite results to return.
- * @return {SuiteResult[]}
- */
- this.suiteResults = function(index, length) {
- return suites.slice(index, index + length);
- };
-
- function storeSuite(result) {
- suites.push(result);
- suites_hash[result.id] = result;
- }
-
- /**
- * Get all of the suites in a single object, with their `id` as the key.
- * @name jsApiReporter#suites
- * @since 2.0.0
- * @function
- * @return {Object} - Map of suite id to {@link SuiteResult}
- */
- this.suites = function() {
- return suites_hash;
- };
-
- const specs = [];
-
- this.specDone = function(result) {
- specs.push(result);
- };
-
- /**
- * Get the results for a set of specs.
- *
- * Retrievable in slices for easier serialization.
- * @name jsApiReporter#specResults
- * @since 2.0.0
- * @function
- * @param {Number} index - The position in the specs list to start from.
- * @param {Number} length - Maximum number of specs results to return.
- * @return {SpecDoneEvent[]}
- */
- this.specResults = function(index, length) {
- return specs.slice(index, index + length);
- };
-
- /**
- * Get all spec results.
- * @name jsApiReporter#specs
- * @since 2.0.0
- * @function
- * @return {SpecDoneEvent[]}
- */
- this.specs = function() {
- return specs;
- };
-
- /**
- * Get the number of milliseconds it took for the full Jasmine suite to run.
- * @name jsApiReporter#executionTime
- * @since 2.0.0
- * @function
- * @return {Number}
- */
- this.executionTime = function() {
- return executionTime;
- };
- }
-
- return JsApiReporter;
-};
-
-getJasmineRequireObj().AllOf = function(j$) {
- 'use strict';
-
- function AllOf() {
- const expectedValues = Array.from(arguments);
- if (expectedValues.length === 0) {
- throw new TypeError(
- 'jasmine.allOf() expects at least one argument to be passed.'
- );
- }
- this.expectedValues = expectedValues;
- }
-
- AllOf.prototype.asymmetricMatch = function(other, matchersUtil) {
- for (const expectedValue of this.expectedValues) {
- if (!matchersUtil.equals(other, expectedValue)) {
- return false;
- }
- }
-
- return true;
- };
-
- AllOf.prototype.jasmineToString = function(pp) {
- return '';
- };
-
- return AllOf;
-};
-
-getJasmineRequireObj().Any = function(j$) {
- 'use strict';
-
- function Any(expectedObject) {
- if (typeof expectedObject === 'undefined') {
- throw new TypeError(
- 'jasmine.any() expects to be passed a constructor function. ' +
- 'Please pass one or use jasmine.anything() to match any object.'
- );
- }
- this.expectedObject = expectedObject;
- }
-
- Any.prototype.asymmetricMatch = function(other) {
- if (this.expectedObject == String) {
- return typeof other == 'string' || other instanceof String;
- }
-
- if (this.expectedObject == Number) {
- return typeof other == 'number' || other instanceof Number;
- }
-
- if (this.expectedObject == Function) {
- return typeof other == 'function' || other instanceof Function;
- }
-
- if (this.expectedObject == Object) {
- return other !== null && typeof other == 'object';
- }
-
- if (this.expectedObject == Boolean) {
- return typeof other == 'boolean';
- }
-
- if (typeof Symbol != 'undefined' && this.expectedObject == Symbol) {
- return typeof other == 'symbol';
- }
-
- return other instanceof this.expectedObject;
- };
-
- Any.prototype.jasmineToString = function() {
- return '';
- };
-
- return Any;
-};
-
-getJasmineRequireObj().Anything = function(j$) {
- 'use strict';
-
- function Anything() {}
-
- Anything.prototype.asymmetricMatch = function(other) {
- return other !== undefined && other !== null;
- };
-
- Anything.prototype.jasmineToString = function() {
- return '';
- };
-
- return Anything;
-};
-
-getJasmineRequireObj().ArrayContaining = function(j$) {
- 'use strict';
-
- function ArrayContaining(sample) {
- this.sample = sample;
- }
-
- ArrayContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
- if (!Array.isArray(this.sample)) {
- throw new Error(
- 'You must provide an array to arrayContaining, not ' +
- j$.private.basicPrettyPrinter(this.sample) +
- '.'
- );
- }
-
- // If the actual parameter is not an array, we can fail immediately, since it couldn't
- // possibly be an "array containing" anything. However, we also want an empty sample
- // array to match anything, so we need to double-check we aren't in that case
- if (!Array.isArray(other) && this.sample.length > 0) {
- return false;
- }
-
- for (const item of this.sample) {
- if (!matchersUtil.contains(other, item)) {
- return false;
- }
- }
-
- return true;
- };
-
- ArrayContaining.prototype.jasmineToString = function(pp) {
- return '';
- };
-
- return ArrayContaining;
-};
-
-getJasmineRequireObj().ArrayWithExactContents = function(j$) {
- 'use strict';
-
- function ArrayWithExactContents(sample) {
- this.sample = sample;
- }
-
- ArrayWithExactContents.prototype.asymmetricMatch = function(
- other,
- matchersUtil
- ) {
- if (!Array.isArray(this.sample)) {
- throw new Error(
- 'You must provide an array to arrayWithExactContents, not ' +
- j$.private.basicPrettyPrinter(this.sample) +
- '.'
- );
- }
-
- if (this.sample.length !== other.length) {
- return false;
- }
-
- for (const item of this.sample) {
- if (!matchersUtil.contains(other, item)) {
- return false;
- }
- }
-
- return true;
- };
-
- ArrayWithExactContents.prototype.jasmineToString = function(pp) {
- return '';
- };
-
- return ArrayWithExactContents;
-};
-
-getJasmineRequireObj().Empty = function(j$) {
- 'use strict';
-
- function Empty() {}
-
- Empty.prototype.asymmetricMatch = function(other) {
- if (
- j$.private.isString(other) ||
- Array.isArray(other) ||
- j$.private.isTypedArray(other)
- ) {
- return other.length === 0;
- }
-
- if (j$.private.isMap(other) || j$.private.isSet(other)) {
- return other.size === 0;
- }
-
- if (j$.private.isObject(other)) {
- return Object.keys(other).length === 0;
- }
- return false;
- };
-
- Empty.prototype.jasmineToString = function() {
- return '';
- };
-
- return Empty;
-};
-
-getJasmineRequireObj().Falsy = function(j$) {
- 'use strict';
-
- function Falsy() {}
-
- Falsy.prototype.asymmetricMatch = function(other) {
- return !other;
- };
-
- Falsy.prototype.jasmineToString = function() {
- return '';
- };
-
- return Falsy;
-};
-
-getJasmineRequireObj().Is = function(j$) {
- 'use strict';
-
- class Is {
- constructor(expected) {
- this.expected_ = expected;
- }
-
- asymmetricMatch(actual) {
- return actual === this.expected_;
- }
-
- jasmineToString(pp) {
- return ``;
- }
- }
-
- return Is;
-};
-
-getJasmineRequireObj().MapContaining = function(j$) {
- 'use strict';
-
- function MapContaining(sample) {
- if (!j$.private.isMap(sample)) {
- throw new Error(
- 'You must provide a map to `mapContaining`, not ' +
- j$.private.basicPrettyPrinter(sample)
- );
- }
-
- this.sample = sample;
- }
-
- MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
- if (!j$.private.isMap(other)) {
- return false;
- }
-
- for (const [key, value] of this.sample) {
- // for each key/value pair in `sample`
- // there should be at least one pair in `other` whose key and value both match
- let hasMatch = false;
- for (const [oKey, oValue] of other) {
- if (
- matchersUtil.equals(oKey, key) &&
- matchersUtil.equals(oValue, value)
- ) {
- hasMatch = true;
- break;
- }
- }
-
- if (!hasMatch) {
- return false;
- }
- }
-
- return true;
- };
-
- MapContaining.prototype.jasmineToString = function(pp) {
- return '';
- };
-
- return MapContaining;
-};
-
-getJasmineRequireObj().NotEmpty = function(j$) {
- 'use strict';
-
- function NotEmpty() {}
-
- NotEmpty.prototype.asymmetricMatch = function(other) {
- if (
- j$.private.isString(other) ||
- Array.isArray(other) ||
- j$.private.isTypedArray(other)
- ) {
- return other.length !== 0;
- }
-
- if (j$.private.isMap(other) || j$.private.isSet(other)) {
- return other.size !== 0;
- }
-
- if (j$.private.isObject(other)) {
- return Object.keys(other).length !== 0;
- }
-
- return false;
- };
-
- NotEmpty.prototype.jasmineToString = function() {
- return '';
- };
-
- return NotEmpty;
-};
-
-getJasmineRequireObj().ObjectContaining = function(j$) {
- 'use strict';
-
- function ObjectContaining(sample) {
- this.sample = sample;
- }
-
- function hasProperty(obj, property) {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (Object.prototype.hasOwnProperty.call(obj, property)) {
- return true;
- }
-
- return hasProperty(Object.getPrototypeOf(obj), property);
- }
-
- ObjectContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
- if (typeof this.sample !== 'object') {
- throw new Error(
- "You must provide an object to objectContaining, not '" +
- this.sample +
- "'."
- );
- }
- if (typeof other !== 'object') {
- return false;
- }
-
- for (const property in this.sample) {
- if (
- !hasProperty(other, property) ||
- !matchersUtil.equals(this.sample[property], other[property])
- ) {
- return false;
- }
- }
-
- return true;
- };
-
- ObjectContaining.prototype.valuesForDiff_ = function(other, pp) {
- if (!j$.private.isObject(other)) {
- return {
- self: this.jasmineToString(pp),
- other: other
- };
- }
-
- const filteredOther = {};
- Object.keys(this.sample).forEach(function(k) {
- // eq short-circuits comparison of objects that have different key sets,
- // so include all keys even if undefined.
- filteredOther[k] = other[k];
- });
-
- return {
- self: this.sample,
- other: filteredOther
- };
- };
-
- ObjectContaining.prototype.jasmineToString = function(pp) {
- return '';
- };
-
- return ObjectContaining;
-};
-
-getJasmineRequireObj().SetContaining = function(j$) {
- 'use strict';
-
- function SetContaining(sample) {
- if (!j$.private.isSet(sample)) {
- throw new Error(
- 'You must provide a set to `setContaining`, not ' +
- j$.private.basicPrettyPrinter(sample)
- );
- }
-
- this.sample = sample;
- }
-
- SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) {
- if (!j$.private.isSet(other)) {
- return false;
- }
-
- for (const item of this.sample) {
- // for each item in `sample` there should be at least one matching item in `other`
- // (not using `matchersUtil.contains` because it compares set members by reference,
- // not by deep value equality)
- let hasMatch = false;
- for (const oItem of other) {
- if (matchersUtil.equals(oItem, item)) {
- hasMatch = true;
- break;
- }
- }
-
- if (!hasMatch) {
- return false;
- }
- }
-
- return true;
- };
-
- SetContaining.prototype.jasmineToString = function(pp) {
- return '';
- };
-
- return SetContaining;
-};
-
-getJasmineRequireObj().StringContaining = function(j$) {
- 'use strict';
-
- function StringContaining(expected) {
- if (!j$.private.isString(expected)) {
- throw new Error('Expected is not a String');
- }
-
- this.expected = expected;
- }
-
- StringContaining.prototype.asymmetricMatch = function(other) {
- if (!j$.private.isString(other)) {
- // Arrays, etc. don't match no matter what their indexOf returns.
- return false;
- }
-
- return other.indexOf(this.expected) !== -1;
- };
-
- StringContaining.prototype.jasmineToString = function() {
- return '';
- };
-
- return StringContaining;
-};
-
-getJasmineRequireObj().StringMatching = function(j$) {
- 'use strict';
-
- function StringMatching(expected) {
- if (!j$.private.isString(expected) && !j$.private.isA('RegExp', expected)) {
- throw new Error('Expected is not a String or a RegExp');
- }
-
- this.regexp = new RegExp(expected);
- }
-
- StringMatching.prototype.asymmetricMatch = function(other) {
- return this.regexp.test(other);
- };
-
- StringMatching.prototype.jasmineToString = function() {
- return '';
- };
-
- return StringMatching;
-};
-
-getJasmineRequireObj().Truthy = function(j$) {
- 'use strict';
-
- function Truthy() {}
-
- Truthy.prototype.asymmetricMatch = function(other) {
- return !!other;
- };
-
- Truthy.prototype.jasmineToString = function() {
- return '';
- };
-
- return Truthy;
-};
-
-//TODO: expectation result may make more sense as a presentation of an expectation.
-getJasmineRequireObj().buildExpectationResult = function(j$) {
- 'use strict';
-
- function buildExpectationResult(options) {
- const exceptionFormatter = new j$.private.ExceptionFormatter();
-
- /**
- * Describes the result of evaluating an expectation
- *
- * @typedef ExpectationResult
- * @property {String} matcherName - The name of the matcher that was executed for this expectation.
- * @property {String} message - The failure message for the expectation.
- * @property {String} stack - The stack trace for the failure if available.
- * @property {Boolean} passed - Whether the expectation passed or failed.
- * @property {String|undefined} globalErrorType - The type of an error that
- * is reported on the top suite. Valid values are undefined, "afterAll",
- * "load", "lateExpectation", and "lateError".
- */
- const result = {
- matcherName: options.matcherName,
- message: message(),
- stack: options.omitStackTrace ? '' : stack(),
- passed: options.passed,
- globalErrorType: options.globalErrorType
- };
-
- if (options.filename !== undefined) {
- result.filename = options.filename;
- }
- if (options.lineno !== undefined) {
- result.lineno = options.lineno;
- }
-
- if (!result.passed) {
- if (options.error && !j$.private.isString(options.error)) {
- if ('code' in options.error) {
- result.code = options.error.code;
- }
-
- if (options.error.code === 'ERR_ASSERTION') {
- result.matcherName = 'assert ' + options.error.operator;
- }
- }
- }
-
- return result;
-
- function message() {
- if (options.passed) {
- return 'Passed.';
- } else if (options.message) {
- return options.message;
- } else if (options.error) {
- return exceptionFormatter.message(options.error);
- }
- return '';
- }
-
- function stack() {
- if (options.passed) {
- return '';
- }
-
- let error = options.error;
-
- if (!error) {
- if (options.errorForStack) {
- error = options.errorForStack;
- } else if (options.stack) {
- error = options;
- } else {
- error = new Error(message());
- }
- }
- // Omit the message from the stack trace because it will be
- // included elsewhere.
- return exceptionFormatter.stack(error, { omitMessage: true });
- }
- }
-
- return buildExpectationResult;
-};
-
-getJasmineRequireObj().CallTracker = function(j$) {
- 'use strict';
-
- /**
- * @namespace Spy#calls
- * @since 2.0.0
- */
- function CallTracker() {
- let calls = [];
- const opts = {};
-
- this.track = function(context) {
- if (opts.cloneArgs) {
- context.args = opts.argsCloner(context.args);
- }
- calls.push(context);
- };
-
- /**
- * Check whether this spy has been invoked.
- * @name Spy#calls#any
- * @since 2.0.0
- * @function
- * @return {Boolean}
- */
- this.any = function() {
- return !!calls.length;
- };
-
- /**
- * Get the number of invocations of this spy.
- * @name Spy#calls#count
- * @since 2.0.0
- * @function
- * @return {Integer}
- */
- this.count = function() {
- return calls.length;
- };
-
- /**
- * Get the arguments that were passed to a specific invocation of this spy.
- * @name Spy#calls#argsFor
- * @since 2.0.0
- * @function
- * @param {Integer} index The 0-based invocation index.
- * @return {Array}
- */
- this.argsFor = function(index) {
- const call = calls[index];
- return call ? call.args : [];
- };
-
- /**
- * Get the "this" object that was passed to a specific invocation of this spy.
- * @name Spy#calls#thisFor
- * @since 3.8.0
- * @function
- * @param {Integer} index The 0-based invocation index.
- * @return {Object?}
- */
- this.thisFor = function(index) {
- const call = calls[index];
- return call ? call.object : undefined;
- };
-
- /**
- * Get the raw calls array for this spy.
- * @name Spy#calls#all
- * @since 2.0.0
- * @function
- * @return {Spy.callData[]}
- */
- this.all = function() {
- return calls;
- };
-
- /**
- * Get all of the arguments for each invocation of this spy in the order they were received.
- * @name Spy#calls#allArgs
- * @since 2.0.0
- * @function
- * @return {Array}
- */
- this.allArgs = function() {
- return calls.map(c => c.args);
- };
-
- /**
- * Get the first invocation of this spy.
- * @name Spy#calls#first
- * @since 2.0.0
- * @function
- * @return {ObjecSpy.callData}
- */
- this.first = function() {
- return calls[0];
- };
-
- /**
- * Get the most recent invocation of this spy.
- * @name Spy#calls#mostRecent
- * @since 2.0.0
- * @function
- * @return {ObjecSpy.callData}
- */
- this.mostRecent = function() {
- return calls[calls.length - 1];
- };
-
- /**
- * Reset this spy as if it has never been called.
- * @name Spy#calls#reset
- * @since 2.0.0
- * @function
- */
- this.reset = function() {
- calls = [];
- };
-
- /**
- * Set this spy to do a clone of arguments passed to each invocation.
- * @name Spy#calls#saveArgumentsByValue
- * @since 2.5.0
- * @param {Function} [argsCloner] A function to use to clone the arguments. Defaults to a shallow cloning function.
- * @function
- */
- this.saveArgumentsByValue = function(
- argsCloner = j$.private.util.cloneArgs
- ) {
- opts.cloneArgs = true;
- opts.argsCloner = argsCloner;
- };
-
- this.unverifiedCount = function() {
- return calls.reduce((count, call) => count + (call.verified ? 0 : 1), 0);
- };
- }
-
- return CallTracker;
-};
-
-getJasmineRequireObj().Clock = function(j$) {
- 'use strict';
-
- /* global process */
- const NODE_JS =
- typeof process !== 'undefined' &&
- process.versions &&
- typeof process.versions.node === 'string';
-
- const IsMockClockTimingFn = Symbol('IsMockClockTimingFn');
-
- /**
- * @class Clock
- * @since 1.3.0
- * @classdesc Jasmine's mock clock is used when testing time dependent code.
- * _Note:_ Do not construct this directly. You can get the current clock with
- * {@link jasmine.clock}.
- * @hideconstructor
- */
- function Clock(global, delayedFunctionSchedulerFactory, mockDate) {
- const realTimingFunctions = {
- setTimeout: global.setTimeout,
- clearTimeout: global.clearTimeout,
- setInterval: global.setInterval,
- clearInterval: global.clearInterval
- };
- const fakeTimingFunctions = {
- setTimeout: setTimeout,
- clearTimeout: clearTimeout,
- setInterval: setInterval,
- clearInterval: clearInterval
- };
- let installed = false;
- let delayedFunctionScheduler;
- let timer;
- // Tracks how the clock ticking behaves.
- // By default, the clock only ticks when the user manually calls a tick method.
- // There is also an 'auto' mode which will advance the clock automatically to
- // to the next task. Once enabled, there is currently no mechanism for users
- // to disable the auto ticking.
- let tickMode = {
- mode: 'manual',
- counter: 0
- };
-
- this.FakeTimeout = FakeTimeout;
-
- /**
- * Install the mock clock over the built-in methods.
- * @name Clock#install
- * @since 2.0.0
- * @function
- * @return {Clock}
- */
- this.install = function() {
- if (!originalTimingFunctionsIntact()) {
- throw new Error(
- 'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?'
- );
- }
- replace(global, fakeTimingFunctions);
- timer = fakeTimingFunctions;
- delayedFunctionScheduler = delayedFunctionSchedulerFactory();
- installed = true;
-
- return this;
- };
-
- /**
- * Uninstall the mock clock, returning the built-in methods to their places.
- * @name Clock#uninstall
- * @since 2.0.0
- * @function
- */
- this.uninstall = function() {
- // Ensure auto ticking loop is aborted when clock is uninstalled
- if (tickMode.mode === 'auto') {
- tickMode = { mode: 'manual', counter: tickMode.counter + 1 };
- }
- delayedFunctionScheduler = null;
- mockDate.uninstall();
- replace(global, realTimingFunctions);
-
- timer = realTimingFunctions;
- installed = false;
- };
-
- /**
- * Execute a function with a mocked Clock
- *
- * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes.
- * @name Clock#withMock
- * @since 2.3.0
- * @function
- * @param {Function} closure The function to be called.
- */
- this.withMock = function(closure) {
- this.install();
- try {
- closure();
- } finally {
- this.uninstall();
- }
- };
-
- /**
- * Instruct the installed Clock to also mock the date returned by `new Date()`
- * @name Clock#mockDate
- * @since 2.1.0
- * @function
- * @param {Date} [initialDate=now] The `Date` to provide.
- */
- this.mockDate = function(initialDate) {
- mockDate.install(initialDate);
- };
-
- this.setTimeout = function(fn, delay, params) {
- return Function.prototype.apply.apply(timer.setTimeout, [
- global,
- arguments
- ]);
- };
-
- this.setInterval = function(fn, delay, params) {
- return Function.prototype.apply.apply(timer.setInterval, [
- global,
- arguments
- ]);
- };
-
- this.clearTimeout = function(id) {
- return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
- };
-
- this.clearInterval = function(id) {
- return Function.prototype.call.apply(timer.clearInterval, [global, id]);
- };
-
- /**
- * Tick the Clock forward, running any enqueued timeouts along the way
- * @name Clock#tick
- * @since 1.3.0
- * @function
- * @param {int} millis The number of milliseconds to tick.
- */
- this.tick = function(millis) {
- if (installed) {
- delayedFunctionScheduler.tick(millis, function(millis) {
- mockDate.tick(millis);
- });
- } else {
- throw new Error(
- 'Mock clock is not installed, use jasmine.clock().install()'
- );
- }
- };
-
- /**
- * Updates the clock to automatically advance time.
- *
- * With this mode, the clock advances to the first scheduled timer and fires it, in a loop.
- * Between each timer, it will also break the event loop, allowing any scheduled promise
-callbacks to execute _before_ running the next one.
- *
- * This mode allows tests to be authored in a way that does not need to be aware of the
- * mock clock. Consequently, tests which have been authored without a mock clock installed
- * can one with auto tick enabled without any other updates to the test logic.
- *
- * In many cases, this can greatly improve test execution speed because asynchronous tasks
- * will execute as quickly as possible rather than waiting real time to complete.
- *
- * Furthermore, tests can be authored in a consistent manner. They can always be written in an asynchronous style
- * rather than having `tick` sprinkled throughout the tests with mock time in order to manually
- * advance the clock.
- *
- * When auto tick is enabled, `tick` can still be used to synchronously advance the clock if necessary.
- * @name Clock#autoTick
- * @function
- * @since 5.7.0
- */
- this.autoTick = function() {
- if (tickMode.mode === 'auto') {
- return;
- }
-
- tickMode = { mode: 'auto', counter: tickMode.counter + 1 };
- advanceUntilModeChanges();
- };
-
- setTimeout[IsMockClockTimingFn] = true;
- clearTimeout[IsMockClockTimingFn] = true;
- setInterval[IsMockClockTimingFn] = true;
- clearInterval[IsMockClockTimingFn] = true;
-
- j$.private.deprecateMonkeyPatching(this);
-
- return this;
-
- // Advances the Clock's time until the mode changes.
- //
- // The time is advanced asynchronously, giving microtasks and events a chance
- // to run before each timer runs.
- //
- // @function
- // @return {!Promise}
- async function advanceUntilModeChanges() {
- if (!installed) {
- throw new Error(
- 'Mock clock is not installed, use jasmine.clock().install()'
- );
- }
- const { counter } = tickMode;
-
- while (true) {
- await newMacrotask();
-
- if (
- tickMode.counter !== counter ||
- !installed ||
- delayedFunctionScheduler === null
- ) {
- return;
- }
-
- if (!delayedFunctionScheduler.isEmpty()) {
- delayedFunctionScheduler.runNextQueuedFunction(function(millis) {
- mockDate.tick(millis);
- });
- }
- }
- }
-
- // Waits until a new macro task.
- //
- // Used with autoTick(), which is meant to act when the test is waiting, we need
- // to insert ourselves in the macro task queue.
- //
- // @return {!Promise}
- async function newMacrotask() {
- if (NODE_JS) {
- // setImmediate is generally faster than setTimeout in Node
- // https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout
- return new Promise(resolve => void setImmediate(resolve));
- }
-
- // MessageChannel ensures that setTimeout is not throttled to 4ms.
- // https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
- // https://stackblitz.com/edit/stackblitz-starters-qtlpcc
- // Note: This trick does not work in Safari, which will still throttle the setTimeout
- const channel = new MessageChannel();
- await new Promise(resolve => {
- channel.port1.onmessage = resolve;
- channel.port2.postMessage(undefined);
- });
- channel.port1.close();
- channel.port2.close();
- // setTimeout ensures that we interleave with other setTimeouts.
- await new Promise(resolve => {
- realTimingFunctions.setTimeout.call(global, resolve);
- });
- }
-
- function originalTimingFunctionsIntact() {
- return (
- global.setTimeout === realTimingFunctions.setTimeout &&
- global.clearTimeout === realTimingFunctions.clearTimeout &&
- global.setInterval === realTimingFunctions.setInterval &&
- global.clearInterval === realTimingFunctions.clearInterval
- );
- }
-
- function replace(dest, source) {
- for (const prop in source) {
- dest[prop] = source[prop];
- }
- }
-
- function setTimeout(fn, delay) {
- if (!NODE_JS) {
- return delayedFunctionScheduler.scheduleFunction(
- fn,
- delay,
- argSlice(arguments, 2)
- );
- }
-
- const timeout = new FakeTimeout();
-
- delayedFunctionScheduler.scheduleFunction(
- fn,
- delay,
- argSlice(arguments, 2),
- false,
- timeout
- );
-
- return timeout;
- }
-
- function clearTimeout(id) {
- return delayedFunctionScheduler.removeFunctionWithId(id);
- }
-
- function setInterval(fn, interval) {
- if (!NODE_JS) {
- return delayedFunctionScheduler.scheduleFunction(
- fn,
- interval,
- argSlice(arguments, 2),
- true
- );
- }
-
- const timeout = new FakeTimeout();
-
- delayedFunctionScheduler.scheduleFunction(
- fn,
- interval,
- argSlice(arguments, 2),
- true,
- timeout
- );
-
- return timeout;
- }
-
- function clearInterval(id) {
- return delayedFunctionScheduler.removeFunctionWithId(id);
- }
-
- function argSlice(argsObj, n) {
- return Array.prototype.slice.call(argsObj, n);
- }
- }
-
- /**
- * Mocks Node.js Timeout class
- */
- function FakeTimeout() {}
-
- FakeTimeout.prototype.ref = function() {
- return this;
- };
-
- FakeTimeout.prototype.unref = function() {
- return this;
- };
-
- Clock.IsMockClockTimingFn = IsMockClockTimingFn;
- return Clock;
-};
-
-getJasmineRequireObj().CompleteOnFirstErrorSkipPolicy = function(j$) {
- 'use strict';
-
- function CompleteOnFirstErrorSkipPolicy(queueableFns) {
- this.queueableFns_ = queueableFns;
- this.erroredFnIx_ = null;
- }
-
- CompleteOnFirstErrorSkipPolicy.prototype.skipTo = function(lastRanFnIx) {
- let i;
-
- for (
- i = lastRanFnIx + 1;
- i < this.queueableFns_.length && this.shouldSkip_(i);
- i++
- ) {}
- return i;
- };
-
- CompleteOnFirstErrorSkipPolicy.prototype.fnErrored = function(fnIx) {
- this.erroredFnIx_ = fnIx;
- };
-
- CompleteOnFirstErrorSkipPolicy.prototype.shouldSkip_ = function(fnIx) {
- if (this.erroredFnIx_ === null) {
- return false;
- }
-
- const fn = this.queueableFns_[fnIx];
- const candidateSuite = fn.suite;
- const errorSuite = this.queueableFns_[this.erroredFnIx_].suite;
- const wasCleanupFn =
- fn.type === 'afterEach' ||
- fn.type === 'afterAll' ||
- fn.type === 'specCleanup';
- return (
- !wasCleanupFn ||
- (candidateSuite && isDescendent(candidateSuite, errorSuite))
- );
- };
-
- function isDescendent(candidate, ancestor) {
- if (!candidate.parentSuite) {
- return false;
- } else if (candidate.parentSuite === ancestor) {
- return true;
- } else {
- return isDescendent(candidate.parentSuite, ancestor);
- }
- }
-
- return CompleteOnFirstErrorSkipPolicy;
-};
-
-getJasmineRequireObj().Configuration = function(j$) {
- 'use strict';
-
- /**
- * This represents the available options to configure Jasmine.
- * Options that are not provided will use their default values.
- * @see Env#configure
- * @interface Configuration
- * @since 3.3.0
- */
- const defaultConfig = {
- /**
- * Whether to randomize spec execution order
- * @name Configuration#random
- * @since 3.3.0
- * @type Boolean
- * @default true
- */
- random: true,
- /**
- * Seed to use as the basis of randomization.
- * Null causes the seed to be determined randomly at the start of execution.
- * @name Configuration#seed
- * @since 3.3.0
- * @type (number|string)
- * @default null
- */
- seed: null,
- /**
- * Whether to stop execution of the suite after the first spec failure
- *
- *
In parallel mode, `stopOnSpecFailure` works on a "best effort"
- * basis. Jasmine will stop execution as soon as practical after a failure
- * but it might not be immediate.