@sebgroup/green-core 2.28.0 → 2.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -141,9 +141,8 @@ var rbcb_toggle_style_default = css`
141
141
  .visually-hidden-checkbox {
142
142
  position: absolute;
143
143
  opacity: 0;
144
- width: 0;
145
- height: 0;
146
- pointer-events: none;
144
+ width: 1px;
145
+ height: 1px;
147
146
  }
148
147
 
149
148
  .rbcb-wrapper {
@@ -0,0 +1,14 @@
1
+ interface AccessibleOptions {
2
+ rules?: Record<string, {
3
+ enabled: boolean;
4
+ }>;
5
+ }
6
+ declare module 'vitest' {
7
+ interface Assertion {
8
+ toBeAccessible(options?: AccessibleOptions): Promise<void>;
9
+ }
10
+ interface AsymmetricMatchersContaining {
11
+ toBeAccessible(options?: AccessibleOptions): void;
12
+ }
13
+ }
14
+ export {};
package/test-setup.js ADDED
@@ -0,0 +1,35 @@
1
+ import * as axe from "axe-core";
2
+ import { afterEach, expect } from "vitest";
3
+ expect.extend({
4
+ async toBeAccessible(received, options = {}) {
5
+ const { rules = {} } = options;
6
+ const results = await axe.run(received, {
7
+ rules: {
8
+ // Disable color-contrast by default as it's often problematic in test environments
9
+ "color-contrast": { enabled: false },
10
+ ...rules
11
+ }
12
+ });
13
+ const violations = results.violations;
14
+ if (violations.length === 0) {
15
+ return {
16
+ pass: true,
17
+ message: () => "Expected element to have accessibility violations"
18
+ };
19
+ }
20
+ const formattedViolations = violations.map((violation) => {
21
+ const nodes = violation.nodes.map((node) => ` - ${node.html}`).join("\n");
22
+ return ` ${violation.id}: ${violation.description}
23
+ ${nodes}`;
24
+ }).join("\n\n");
25
+ return {
26
+ pass: false,
27
+ message: () => `Expected element to be accessible, but found the following violations:
28
+
29
+ ${formattedViolations}`
30
+ };
31
+ }
32
+ });
33
+ afterEach(() => {
34
+ document.body.innerHTML = "";
35
+ });
@@ -1,6 +1,6 @@
1
1
  import "../../chunks/chunk.QU3DSPNU.js";
2
2
  import { html as litHtml } from "lit";
3
- const VER_SUFFIX = "-b8f4f0";
3
+ const VER_SUFFIX = "-712748";
4
4
  class ScopedElementRegistry {
5
5
  static get instance() {
6
6
  if (!globalThis.__gdsElementLookupTable?.[VER_SUFFIX])
@@ -1,32 +1,64 @@
1
- /** A testing utility that measures an element's position and clicks on it. */
1
+ import { html, TemplateResult } from 'lit';
2
+ export { html };
3
+ /**
4
+ * Renders a lit template into a container and returns the first element child.
5
+ * Replacement for @open-wc/testing fixture function.
6
+ */
7
+ export declare function fixture<T extends Element>(template: TemplateResult): Promise<T>;
8
+ /**
9
+ * Wait for a specified number of milliseconds.
10
+ * Replacement for @open-wc/testing aTimeout function.
11
+ */
12
+ export declare function aTimeout(ms: number): Promise<void>;
13
+ /**
14
+ * Wait until a condition becomes true or timeout.
15
+ * Replacement for @open-wc/testing waitUntil function.
16
+ */
17
+ export declare function waitUntil(condition: () => boolean | Promise<boolean>, message?: string, options?: {
18
+ interval?: number;
19
+ timeout?: number;
20
+ }): Promise<void>;
21
+ /** A testing utility that clicks on an element. */
2
22
  export declare function clickOnElement(
3
23
  /** The element to click */
4
24
  el: Element,
5
- /** The location of the element to click */
6
- position?: 'top' | 'right' | 'bottom' | 'left' | 'center',
7
- /** The horizontal offset to apply to the position when clicking */
8
- offsetX?: number,
9
- /** The vertical offset to apply to the position when clicking */
10
- offsetY?: number): Promise<void>;
25
+ /** The location of the element to click (ignored for now, clicks center) */
26
+ _position?: 'top' | 'right' | 'bottom' | 'left' | 'center',
27
+ /** The horizontal offset to apply to the position when clicking (ignored) */
28
+ _offsetX?: number,
29
+ /** The vertical offset to apply to the position when clicking (ignored) */
30
+ _offsetY?: number): Promise<void>;
11
31
  /** A testing utility that moves the mouse onto an element. */
12
32
  export declare function moveMouseOnElement(
13
- /** The element to click */
33
+ /** The element to hover */
14
34
  el: Element,
15
- /** The location of the element to click */
16
- position?: 'top' | 'right' | 'bottom' | 'left' | 'center',
17
- /** The horizontal offset to apply to the position when clicking */
18
- offsetX?: number,
19
- /** The vertical offset to apply to the position when clicking */
20
- offsetY?: number): Promise<void>;
35
+ /** The location of the element (ignored for now, hovers center) */
36
+ _position?: 'top' | 'right' | 'bottom' | 'left' | 'center',
37
+ /** The horizontal offset to apply (ignored) */
38
+ _offsetX?: number,
39
+ /** The vertical offset to apply (ignored) */
40
+ _offsetY?: number): Promise<void>;
21
41
  /** A testing utility that drags an element with the mouse. */
22
42
  export declare function dragElement(
23
43
  /** The element to drag */
24
- el: Element,
44
+ source: Element,
25
45
  /** The horizontal distance to drag in pixels */
26
46
  deltaX?: number,
27
47
  /** The vertical distance to drag in pixels */
28
48
  deltaY?: number): Promise<void>;
29
49
  export declare function isWebKit(): boolean;
50
+ /**
51
+ * WebKit-aware Tab helper.
52
+ * On WebKit, use Alt+Tab to move focus
53
+ */
54
+ export declare function tabNext(options?: {
55
+ shift?: boolean;
56
+ }): Promise<void>;
30
57
  export declare function isChromium(): boolean;
31
58
  export declare function isFirefox(): boolean;
32
59
  export declare function onlyDate(date: Date | undefined): string | undefined;
60
+ /** A helper to wait for a specific amount of time */
61
+ export declare function timeout(ms: number): Promise<void>;
62
+ /** A helper to wait for a condition to become true */
63
+ export declare function conditionToBeTrue(condition: () => boolean, timeoutMs?: number): Promise<void>;
64
+ export declare function setViewportSize(width: number, height: number): Promise<() => Promise<void>>;
@@ -1,69 +1,121 @@
1
1
  import "../../chunks/chunk.QU3DSPNU.js";
2
- import { sendMouse } from "@web/test-runner-commands";
3
- function determineMousePosition(el, position, offsetX, offsetY) {
4
- const { x, y, width, height } = el.getBoundingClientRect();
5
- const centerX = Math.floor(x + window.scrollX + width / 2);
6
- const centerY = Math.floor(y + window.scrollY + height / 2);
7
- let clickX;
8
- let clickY;
2
+ import { html, render } from "lit";
3
+ import { page, userEvent } from "@vitest/browser/context";
4
+ async function fixture(template) {
5
+ const container = document.createElement("div");
6
+ document.body.appendChild(container);
7
+ render(template, container);
8
+ await new Promise((resolve) => setTimeout(resolve, 0));
9
+ await Promise.resolve();
10
+ const element = container.firstElementChild;
11
+ return element;
12
+ }
13
+ function aTimeout(ms) {
14
+ return new Promise((resolve) => setTimeout(resolve, ms));
15
+ }
16
+ async function waitUntil(condition, message, options = {}) {
17
+ const { interval = 50, timeout: timeout2 = 1e3 } = options;
18
+ const startTime = Date.now();
19
+ while (Date.now() - startTime <= timeout2) {
20
+ const result = await condition();
21
+ if (result) return;
22
+ await new Promise((resolve) => setTimeout(resolve, interval));
23
+ }
24
+ throw new Error(message || `waitUntil timed out after ${timeout2}ms`);
25
+ }
26
+ async function clickOnElement(el, _position = "center", _offsetX = 0, _offsetY = 0) {
27
+ const rect = el.getBoundingClientRect();
28
+ const position = getClickPosition(rect, _position, _offsetX, _offsetY);
29
+ try {
30
+ await withTimeout(userEvent.click(el, { position }), 1e3);
31
+ } catch (error) {
32
+ await withTimeout(userEvent.click(el, { position, force: true }), 1e3);
33
+ }
34
+ }
35
+ async function withTimeout(promise, ms) {
36
+ let timeoutId;
37
+ const timeout2 = new Promise((_, reject) => {
38
+ timeoutId = setTimeout(() => {
39
+ reject(new Error(`click timed out after ${ms}ms`));
40
+ }, ms);
41
+ });
42
+ try {
43
+ return await Promise.race([promise, timeout2]);
44
+ } finally {
45
+ if (timeoutId) clearTimeout(timeoutId);
46
+ }
47
+ }
48
+ function getClickPosition(rect, position, offsetX, offsetY) {
49
+ const centerX = rect.width / 2;
50
+ const centerY = rect.height / 2;
51
+ let x = centerX;
52
+ let y = centerY;
9
53
  switch (position) {
10
54
  case "top":
11
- clickX = centerX;
12
- clickY = y;
13
- break;
14
- case "right":
15
- clickX = x + width - 1;
16
- clickY = centerY;
55
+ y = 1;
17
56
  break;
18
57
  case "bottom":
19
- clickX = centerX;
20
- clickY = y + height - 1;
58
+ y = Math.max(1, rect.height - 1);
21
59
  break;
22
60
  case "left":
23
- clickX = x;
24
- clickY = centerY;
61
+ x = 1;
25
62
  break;
63
+ case "right":
64
+ x = Math.max(1, rect.width - 1);
65
+ break;
66
+ case "center":
26
67
  default:
27
- clickX = centerX;
28
- clickY = centerY;
68
+ break;
29
69
  }
30
- clickX += offsetX;
31
- clickY += offsetY;
32
- return { clickX, clickY };
33
- }
34
- async function clickOnElement(el, position = "center", offsetX = 0, offsetY = 0) {
35
- const { clickX, clickY } = determineMousePosition(
36
- el,
37
- position,
38
- offsetX,
39
- offsetY
40
- );
41
- await sendMouse({ type: "click", position: [clickX, clickY] });
42
- }
43
- async function moveMouseOnElement(el, position = "center", offsetX = 0, offsetY = 0) {
44
- const { clickX, clickY } = determineMousePosition(
45
- el,
46
- position,
47
- offsetX,
48
- offsetY
49
- );
50
- await sendMouse({ type: "move", position: [clickX, clickY] });
51
- }
52
- async function dragElement(el, deltaX = 0, deltaY = 0) {
53
- await moveMouseOnElement(el);
54
- await sendMouse({ type: "down" });
55
- const { clickX, clickY } = determineMousePosition(
56
- el,
57
- "center",
58
- deltaX,
59
- deltaY
60
- );
61
- await sendMouse({ type: "move", position: [clickX, clickY] });
62
- await sendMouse({ type: "up" });
70
+ return {
71
+ x: Math.max(1, x + offsetX),
72
+ y: Math.max(1, y + offsetY)
73
+ };
74
+ }
75
+ async function moveMouseOnElement(el, _position = "center", _offsetX = 0, _offsetY = 0) {
76
+ await userEvent.hover(el);
77
+ }
78
+ async function dragElement(source, deltaX = 0, deltaY = 0) {
79
+ const rect = source.getBoundingClientRect();
80
+ const targetX = rect.left + rect.width / 2 + deltaX;
81
+ const targetY = rect.top + rect.height / 2 + deltaY;
82
+ await userEvent.hover(source);
83
+ const event = new MouseEvent("mousedown", {
84
+ bubbles: true,
85
+ cancelable: true,
86
+ clientX: rect.left + rect.width / 2,
87
+ clientY: rect.top + rect.height / 2
88
+ });
89
+ source.dispatchEvent(event);
90
+ const moveEvent = new MouseEvent("mousemove", {
91
+ bubbles: true,
92
+ cancelable: true,
93
+ clientX: targetX,
94
+ clientY: targetY
95
+ });
96
+ source.dispatchEvent(moveEvent);
97
+ const upEvent = new MouseEvent("mouseup", {
98
+ bubbles: true,
99
+ cancelable: true,
100
+ clientX: targetX,
101
+ clientY: targetY
102
+ });
103
+ source.dispatchEvent(upEvent);
63
104
  }
64
105
  function isWebKit() {
65
106
  return navigator.userAgent.toLowerCase().indexOf("safari") > -1 && navigator.userAgent.toLowerCase().indexOf("chrome") < 0;
66
107
  }
108
+ async function tabNext(options) {
109
+ if (isWebKit()) {
110
+ if (options?.shift) {
111
+ await userEvent.keyboard("{Alt>}{Shift>}{Tab}{/Shift}{/Alt}");
112
+ return;
113
+ }
114
+ await userEvent.keyboard("{Alt>}{Tab}{/Alt}");
115
+ return;
116
+ }
117
+ await userEvent.tab(options);
118
+ }
67
119
  function isChromium() {
68
120
  return navigator.userAgent.toLowerCase().indexOf("chrome") > -1;
69
121
  }
@@ -74,12 +126,52 @@ function onlyDate(date) {
74
126
  if (!date) return void 0;
75
127
  return date.toISOString().split("T")[0];
76
128
  }
129
+ function timeout(ms) {
130
+ return new Promise((resolve) => setTimeout(resolve, ms));
131
+ }
132
+ function conditionToBeTrue(condition, timeoutMs = 1e3) {
133
+ return new Promise((resolve, reject) => {
134
+ const startTime = Date.now();
135
+ const check = () => {
136
+ if (condition()) {
137
+ resolve();
138
+ } else if (Date.now() - startTime > timeoutMs) {
139
+ reject(new Error("Condition was not met within timeout"));
140
+ } else {
141
+ requestAnimationFrame(check);
142
+ }
143
+ };
144
+ check();
145
+ });
146
+ }
147
+ async function setViewportSize(width, height) {
148
+ const originalSize = {
149
+ width: window.innerWidth,
150
+ height: window.innerHeight
151
+ };
152
+ if (page) {
153
+ await page.viewport(width, height);
154
+ }
155
+ return async () => {
156
+ if (page) {
157
+ await page.viewport(originalSize.width, originalSize.height);
158
+ }
159
+ };
160
+ }
77
161
  export {
162
+ aTimeout,
78
163
  clickOnElement,
164
+ conditionToBeTrue,
79
165
  dragElement,
166
+ fixture,
167
+ html,
80
168
  isChromium,
81
169
  isFirefox,
82
170
  isWebKit,
83
171
  moveMouseOnElement,
84
- onlyDate
172
+ onlyDate,
173
+ setViewportSize,
174
+ tabNext,
175
+ timeout,
176
+ waitUntil
85
177
  };