@startinblox/components-ds4go 2.3.0 → 3.0.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.
- package/.gitlab-ci.yml +8 -2
- package/AGENTS.md +516 -0
- package/biome.json +1 -1
- package/cypress/component/no-component-test.cy.ts +9 -0
- package/cypress/e2e/helpers/components/setupCacheInvalidation.cy.ts +512 -0
- package/cypress/e2e/helpers/components/setupCacheOnResourceReady.cy.ts +483 -0
- package/cypress/e2e/helpers/components/setupComponentSubscriptions.cy.ts +239 -0
- package/cypress/e2e/helpers/components/setupOnSaveReset.cy.ts +380 -0
- package/cypress/e2e/helpers/datas/checkValueInIntervalRecursive.cy.ts +563 -0
- package/cypress/e2e/helpers/datas/dataBuilder.cy.ts +508 -0
- package/cypress/e2e/helpers/datas/filterGenerator.cy.ts +285 -0
- package/cypress/e2e/helpers/datas/filterObjectByDateAfter.cy.ts +389 -0
- package/cypress/e2e/helpers/datas/filterObjectByDateInterval.cy.ts +613 -0
- package/cypress/e2e/helpers/datas/filterObjectById.cy.ts +276 -0
- package/cypress/e2e/helpers/datas/filterObjectByInterval.cy.ts +237 -0
- package/cypress/e2e/helpers/datas/filterObjectByNamedValue.cy.ts +299 -0
- package/cypress/e2e/helpers/datas/filterObjectByType.cy.ts +307 -0
- package/cypress/e2e/helpers/datas/filterObjectByValue.cy.ts +375 -0
- package/cypress/e2e/helpers/datas/sort.cy.ts +293 -0
- package/cypress/e2e/helpers/ui/formatDate.cy.ts +233 -0
- package/cypress/e2e/helpers/utils/requestNavigation.cy.ts +257 -0
- package/cypress/e2e/helpers/utils/uniq.cy.ts +160 -0
- package/cypress/support/e2e.ts +1 -0
- package/cypress.config.ts +2 -0
- package/dist/index.js +1113 -1004
- package/package.json +10 -10
- package/src/components/solid-boilerplate.ts +76 -0
- package/src/helpers/components/componentObjectHandler.ts +5 -7
- package/src/helpers/components/componentObjectsHandler.ts +8 -3
- package/src/helpers/components/orbitComponent.ts +87 -68
- package/src/helpers/components/orbitDspComponent.ts +14 -4
- package/src/helpers/components/setupCacheInvalidation.ts +50 -23
- package/src/helpers/components/setupCacheOnResourceReady.ts +42 -23
- package/src/helpers/components/setupComponentSubscriptions.ts +10 -9
- package/src/helpers/components/setupOnSaveReset.ts +27 -5
- package/src/helpers/datas/checkValueInIntervalRecursive.ts +66 -0
- package/src/helpers/datas/dataBuilder.ts +4 -4
- package/src/helpers/datas/filterGenerator.ts +13 -10
- package/src/helpers/datas/filterObjectByDateAfter.ts +3 -3
- package/src/helpers/datas/filterObjectByDateInterval.ts +44 -0
- package/src/helpers/datas/filterObjectById.ts +7 -6
- package/src/helpers/datas/filterObjectByInterval.ts +6 -110
- package/src/helpers/datas/filterObjectByNamedValue.ts +35 -33
- package/src/helpers/datas/filterObjectByType.ts +3 -3
- package/src/helpers/datas/filterObjectByValue.ts +17 -16
- package/src/helpers/datas/sort.ts +50 -23
- package/src/helpers/index.ts +2 -0
- package/src/helpers/ui/formatDate.ts +14 -1
- package/src/helpers/utils/requestNavigation.ts +5 -2
- package/src/helpers/utils/uniq.ts +1 -1
- package/cypress/component/solid-boilerplate.cy.ts +0 -9
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import formatDate from "@helpers/ui/formatDate";
|
|
2
|
+
|
|
3
|
+
describe("formatDate helper function", () => {
|
|
4
|
+
describe("Default format", () => {
|
|
5
|
+
it("should format date with default format (2-digit day, month, year)", () => {
|
|
6
|
+
const date = new Date("2023-12-25");
|
|
7
|
+
const result = formatDate(date);
|
|
8
|
+
expect(result).to.match(/^\d{2}\/\d{2}\/\d{2}$/);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("should format date string with default format", () => {
|
|
12
|
+
const result = formatDate("2023-12-25");
|
|
13
|
+
expect(result).to.match(/^\d{2}\/\d{2}\/\d{2}$/);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should format date with leading zeros", () => {
|
|
17
|
+
const result = formatDate("2023-01-05");
|
|
18
|
+
expect(result).to.contain("01");
|
|
19
|
+
expect(result).to.contain("05");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("Custom formats", () => {
|
|
24
|
+
it("should format with long date style", () => {
|
|
25
|
+
const result = formatDate("2023-12-25", {
|
|
26
|
+
dateStyle: "long",
|
|
27
|
+
});
|
|
28
|
+
expect(result.length).to.be.greaterThan(10);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should format with short date style", () => {
|
|
32
|
+
const result = formatDate("2023-12-25", {
|
|
33
|
+
dateStyle: "short",
|
|
34
|
+
});
|
|
35
|
+
expect(result.length).to.be.lessThan(15);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should format with specific options", () => {
|
|
39
|
+
const result = formatDate("2023-12-25", {
|
|
40
|
+
weekday: "long",
|
|
41
|
+
year: "numeric",
|
|
42
|
+
month: "long",
|
|
43
|
+
day: "numeric",
|
|
44
|
+
});
|
|
45
|
+
expect(result).to.include("2023");
|
|
46
|
+
expect(result.length).to.be.greaterThan(10);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should format with day and month only", () => {
|
|
50
|
+
const result = formatDate("2023-12-25", {
|
|
51
|
+
day: "2-digit",
|
|
52
|
+
month: "short",
|
|
53
|
+
});
|
|
54
|
+
expect(result).to.not.include("2023");
|
|
55
|
+
expect(result.length).to.be.lessThan(10);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should format with time options", () => {
|
|
59
|
+
const result = formatDate("2023-12-25T14:30:00", {
|
|
60
|
+
hour: "2-digit",
|
|
61
|
+
minute: "2-digit",
|
|
62
|
+
second: "2-digit",
|
|
63
|
+
});
|
|
64
|
+
// Not testing hours as it depends on the time-formatting of the system
|
|
65
|
+
expect(result).to.include("30");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should format with combined date and time", () => {
|
|
69
|
+
const result = formatDate("2023-12-25T14:30:00", {
|
|
70
|
+
day: "2-digit",
|
|
71
|
+
month: "2-digit",
|
|
72
|
+
year: "numeric",
|
|
73
|
+
hour: "2-digit",
|
|
74
|
+
minute: "2-digit",
|
|
75
|
+
});
|
|
76
|
+
expect(result).to.include("2023");
|
|
77
|
+
expect(result).to.include("30");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("Input types", () => {
|
|
82
|
+
it("should handle Date object", () => {
|
|
83
|
+
const date = new Date("2023-12-25");
|
|
84
|
+
const result = formatDate(date);
|
|
85
|
+
expect(result).to.be.a("string");
|
|
86
|
+
expect(result).to.not.equal("");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should handle ISO date string", () => {
|
|
90
|
+
const result = formatDate("2023-12-25");
|
|
91
|
+
expect(result).to.be.a("string");
|
|
92
|
+
expect(result).to.not.equal("");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should handle ISO datetime string", () => {
|
|
96
|
+
const result = formatDate("2023-12-25T14:30:00.000Z");
|
|
97
|
+
expect(result).to.be.a("string");
|
|
98
|
+
expect(result).to.not.equal("");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should handle various date string formats", () => {
|
|
102
|
+
const result1 = formatDate("2023/12/25");
|
|
103
|
+
const result2 = formatDate("December 25, 2023");
|
|
104
|
+
expect(result1).to.be.a("string");
|
|
105
|
+
expect(result2).to.be.a("string");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should throw error for invalid timestamp string", () => {
|
|
109
|
+
const timestamp = "invalid-timestamp";
|
|
110
|
+
expect(() => formatDate(timestamp)).to.throw();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe("Edge cases and error handling", () => {
|
|
115
|
+
it("should return empty string for undefined input", () => {
|
|
116
|
+
const result = formatDate(undefined);
|
|
117
|
+
expect(result).to.equal("");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should return empty string for empty string", () => {
|
|
121
|
+
const result = formatDate("");
|
|
122
|
+
expect(result).to.equal("");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("should throw error for invalid date string", () => {
|
|
126
|
+
expect(() => formatDate("invalid-date")).to.throw();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("should throw error for malformed date", () => {
|
|
130
|
+
expect(() => formatDate("not-a-date")).to.throw();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should handle very old dates", () => {
|
|
134
|
+
const result = formatDate("1900-01-01");
|
|
135
|
+
expect(result).to.be.a("string");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("should handle future dates", () => {
|
|
139
|
+
const result = formatDate("2050-12-31");
|
|
140
|
+
expect(result).to.be.a("string");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should handle dates with time zones", () => {
|
|
144
|
+
const result = formatDate("2023-12-25T14:30:00+05:30");
|
|
145
|
+
expect(result).to.be.a("string");
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe("Date edge cases", () => {
|
|
150
|
+
it("should handle leap year dates", () => {
|
|
151
|
+
const result = formatDate("2024-02-29");
|
|
152
|
+
expect(result).to.be.a("string");
|
|
153
|
+
expect(result).to.not.equal("");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should handle last day of month", () => {
|
|
157
|
+
const result = formatDate("2023-12-31");
|
|
158
|
+
expect(result).to.be.a("string");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("should handle first day of month", () => {
|
|
162
|
+
const result = formatDate("2023-01-01");
|
|
163
|
+
expect(result).to.be.a("string");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("should handle end of year", () => {
|
|
167
|
+
const result = formatDate("2023-12-31");
|
|
168
|
+
expect(result).to.be.a("string");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("should handle beginning of year", () => {
|
|
172
|
+
const result = formatDate("2023-01-01");
|
|
173
|
+
expect(result).to.be.a("string");
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe("Consistency", () => {
|
|
178
|
+
it("should return same format for same date multiple times", () => {
|
|
179
|
+
const date = "2023-12-25";
|
|
180
|
+
const result1 = formatDate(date);
|
|
181
|
+
const result2 = formatDate(date);
|
|
182
|
+
expect(result1).to.equal(result2);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("should handle dates with milliseconds precision", () => {
|
|
186
|
+
const date = new Date("2023-12-25T14:30:00.123Z");
|
|
187
|
+
const result = formatDate(date);
|
|
188
|
+
expect(result).to.be.a("string");
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("should format different dates distinctly", () => {
|
|
192
|
+
const result1 = formatDate("2023-01-15");
|
|
193
|
+
const result2 = formatDate("2023-12-15");
|
|
194
|
+
expect(result1).to.not.equal(result2);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe("Format options variations", () => {
|
|
199
|
+
it("should handle numeric month", () => {
|
|
200
|
+
const result = formatDate("2023-12-25", {
|
|
201
|
+
month: "numeric",
|
|
202
|
+
day: "numeric",
|
|
203
|
+
year: "numeric",
|
|
204
|
+
});
|
|
205
|
+
expect(result).to.be.a("string");
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("should handle 2-digit month", () => {
|
|
209
|
+
const result = formatDate("2023-12-25", {
|
|
210
|
+
month: "2-digit",
|
|
211
|
+
day: "2-digit",
|
|
212
|
+
year: "2-digit",
|
|
213
|
+
});
|
|
214
|
+
expect(result).to.be.a("string");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("should handle narrow weekday", () => {
|
|
218
|
+
const result = formatDate("2023-12-25", {
|
|
219
|
+
weekday: "narrow",
|
|
220
|
+
});
|
|
221
|
+
expect(result).to.be.a("string");
|
|
222
|
+
expect(result.length).to.be.lessThan(5);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("should handle short weekday", () => {
|
|
226
|
+
const result = formatDate("2023-12-25", {
|
|
227
|
+
weekday: "short",
|
|
228
|
+
});
|
|
229
|
+
expect(result).to.be.a("string");
|
|
230
|
+
expect(result.length).to.be.lessThan(10);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import requestNavigation from "@helpers/utils/requestNavigation";
|
|
2
|
+
|
|
3
|
+
describe("requestNavigation helper function", () => {
|
|
4
|
+
describe("Event dispatching", () => {
|
|
5
|
+
it("should dispatch requestNavigation event", (done) => {
|
|
6
|
+
const handler = (e: Event) => {
|
|
7
|
+
expect(e.type).to.equal("requestNavigation");
|
|
8
|
+
window.removeEventListener("requestNavigation", handler);
|
|
9
|
+
done();
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
window.addEventListener("requestNavigation", handler);
|
|
13
|
+
requestNavigation("/some-route");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should dispatch event with correct route in detail", (done) => {
|
|
17
|
+
const route = "/test-route";
|
|
18
|
+
|
|
19
|
+
const handler = (e: Event) => {
|
|
20
|
+
const customEvent = e as CustomEvent;
|
|
21
|
+
expect(customEvent.detail).to.have.property("route", route);
|
|
22
|
+
window.removeEventListener("requestNavigation", handler);
|
|
23
|
+
done();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
window.addEventListener("requestNavigation", handler);
|
|
27
|
+
requestNavigation(route);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should dispatch event with resource object when resource is string", (done) => {
|
|
31
|
+
const resourceId = "https://example.com/resource/123";
|
|
32
|
+
|
|
33
|
+
const handler = (e: Event) => {
|
|
34
|
+
const customEvent = e as CustomEvent;
|
|
35
|
+
expect(customEvent.detail).to.have.property("resource");
|
|
36
|
+
expect(customEvent.detail.resource).to.be.an("object");
|
|
37
|
+
expect(customEvent.detail.resource).to.have.property("@id", resourceId);
|
|
38
|
+
window.removeEventListener("requestNavigation", handler);
|
|
39
|
+
done();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
window.addEventListener("requestNavigation", handler);
|
|
43
|
+
requestNavigation("/route", resourceId);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should dispatch event with false when resource is not provided", (done) => {
|
|
47
|
+
const handler = (e: Event) => {
|
|
48
|
+
const customEvent = e as CustomEvent;
|
|
49
|
+
expect(customEvent.detail).to.have.property("resource");
|
|
50
|
+
expect(customEvent.detail.resource).to.equal(false);
|
|
51
|
+
window.removeEventListener("requestNavigation", handler);
|
|
52
|
+
done();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
window.addEventListener("requestNavigation", handler);
|
|
56
|
+
requestNavigation("/route");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should dispatch event with false when resource is explicitly false", (done) => {
|
|
60
|
+
const handler = (e: Event) => {
|
|
61
|
+
const customEvent = e as CustomEvent;
|
|
62
|
+
expect(customEvent.detail).to.have.property("resource");
|
|
63
|
+
expect(customEvent.detail.resource).to.equal(false);
|
|
64
|
+
window.removeEventListener("requestNavigation", handler);
|
|
65
|
+
done();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
window.addEventListener("requestNavigation", handler);
|
|
69
|
+
requestNavigation("/route", false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should dispatch event only once per call", (done) => {
|
|
73
|
+
let callCount = 0;
|
|
74
|
+
|
|
75
|
+
const handler = () => {
|
|
76
|
+
callCount++;
|
|
77
|
+
if (callCount === 1) {
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
expect(callCount).to.equal(1);
|
|
80
|
+
window.removeEventListener("requestNavigation", handler);
|
|
81
|
+
done();
|
|
82
|
+
}, 10);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
window.addEventListener("requestNavigation", handler);
|
|
87
|
+
requestNavigation("/route");
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("Multiple calls", () => {
|
|
92
|
+
it("should dispatch event for each call", (done) => {
|
|
93
|
+
const routes = ["/route1", "/route2", "/route3"];
|
|
94
|
+
let eventCount = 0;
|
|
95
|
+
|
|
96
|
+
const handler = (e: Event) => {
|
|
97
|
+
const customEvent = e as CustomEvent;
|
|
98
|
+
expect(customEvent.detail.route).to.equal(routes[eventCount]);
|
|
99
|
+
eventCount++;
|
|
100
|
+
|
|
101
|
+
if (eventCount === routes.length) {
|
|
102
|
+
window.removeEventListener("requestNavigation", handler);
|
|
103
|
+
done();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
window.addEventListener("requestNavigation", handler);
|
|
108
|
+
for (const route of routes) {
|
|
109
|
+
requestNavigation(route);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should handle rapid consecutive calls", (done) => {
|
|
114
|
+
const callCount = 100;
|
|
115
|
+
let eventCount = 0;
|
|
116
|
+
|
|
117
|
+
const handler = () => {
|
|
118
|
+
eventCount++;
|
|
119
|
+
if (eventCount === callCount) {
|
|
120
|
+
window.removeEventListener("requestNavigation", handler);
|
|
121
|
+
done();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
window.addEventListener("requestNavigation", handler);
|
|
126
|
+
for (let i = 0; i < callCount; i++) {
|
|
127
|
+
requestNavigation(`/route${i}`);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("Event detail structure", () => {
|
|
133
|
+
it("should have detail property on event", (done) => {
|
|
134
|
+
const handler = (e: Event) => {
|
|
135
|
+
const customEvent = e as CustomEvent;
|
|
136
|
+
expect(customEvent).to.have.property("detail");
|
|
137
|
+
expect(customEvent.detail).to.be.an("object");
|
|
138
|
+
window.removeEventListener("requestNavigation", handler);
|
|
139
|
+
done();
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
window.addEventListener("requestNavigation", handler);
|
|
143
|
+
requestNavigation("/route");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should have both route and resource in detail", (done) => {
|
|
147
|
+
const handler = (e: Event) => {
|
|
148
|
+
const customEvent = e as CustomEvent;
|
|
149
|
+
expect(customEvent.detail).to.have.all.keys("route", "resource");
|
|
150
|
+
window.removeEventListener("requestNavigation", handler);
|
|
151
|
+
done();
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
window.addEventListener("requestNavigation", handler);
|
|
155
|
+
requestNavigation("/route", "https://example.com/id");
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe("Type handling", () => {
|
|
160
|
+
it("should convert string resource to object with @id key", (done) => {
|
|
161
|
+
const resourceId = "https://example.com/resource/456";
|
|
162
|
+
|
|
163
|
+
const handler = (e: Event) => {
|
|
164
|
+
const customEvent = e as CustomEvent;
|
|
165
|
+
expect(customEvent.detail.resource).to.deep.equal({
|
|
166
|
+
"@id": resourceId,
|
|
167
|
+
});
|
|
168
|
+
window.removeEventListener("requestNavigation", handler);
|
|
169
|
+
done();
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
window.addEventListener("requestNavigation", handler);
|
|
173
|
+
requestNavigation("/route", resourceId);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should keep false as false for resource parameter", (done) => {
|
|
177
|
+
const handler = (e: Event) => {
|
|
178
|
+
const customEvent = e as CustomEvent;
|
|
179
|
+
expect(customEvent.detail.resource).to.equal(false);
|
|
180
|
+
window.removeEventListener("requestNavigation", handler);
|
|
181
|
+
done();
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
window.addEventListener("requestNavigation", handler);
|
|
185
|
+
requestNavigation("/route", false);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("should pass false when resource parameter is undefined", (done) => {
|
|
189
|
+
const handler = (e: Event) => {
|
|
190
|
+
const customEvent = e as CustomEvent;
|
|
191
|
+
expect(customEvent.detail.resource).to.equal(false);
|
|
192
|
+
window.removeEventListener("requestNavigation", handler);
|
|
193
|
+
done();
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
window.addEventListener("requestNavigation", handler);
|
|
197
|
+
requestNavigation("/route", undefined as any);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe("Edge cases", () => {
|
|
202
|
+
it("should handle empty string route", (done) => {
|
|
203
|
+
const handler = (e: Event) => {
|
|
204
|
+
const customEvent = e as CustomEvent;
|
|
205
|
+
expect(customEvent.detail.route).to.equal("");
|
|
206
|
+
window.removeEventListener("requestNavigation", handler);
|
|
207
|
+
done();
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
window.addEventListener("requestNavigation", handler);
|
|
211
|
+
requestNavigation("");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("should handle route with special characters", (done) => {
|
|
215
|
+
const route = "/route/with/special-chars?query=1¶m=value#hash";
|
|
216
|
+
|
|
217
|
+
const handler = (e: Event) => {
|
|
218
|
+
const customEvent = e as CustomEvent;
|
|
219
|
+
expect(customEvent.detail.route).to.equal(route);
|
|
220
|
+
window.removeEventListener("requestNavigation", handler);
|
|
221
|
+
done();
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
window.addEventListener("requestNavigation", handler);
|
|
225
|
+
requestNavigation(route);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("should handle empty string resource", (done) => {
|
|
229
|
+
const handler = (e: Event) => {
|
|
230
|
+
const customEvent = e as CustomEvent;
|
|
231
|
+
expect(customEvent.detail.resource).to.deep.equal({ "@id": "" });
|
|
232
|
+
window.removeEventListener("requestNavigation", handler);
|
|
233
|
+
done();
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
window.addEventListener("requestNavigation", handler);
|
|
237
|
+
requestNavigation("/route", "");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("should handle resource URL with special characters", (done) => {
|
|
241
|
+
const resourceId =
|
|
242
|
+
"https://example.com/resource?query=1¶m=value#hash";
|
|
243
|
+
|
|
244
|
+
const handler = (e: Event) => {
|
|
245
|
+
const customEvent = e as CustomEvent;
|
|
246
|
+
expect(customEvent.detail.resource).to.deep.equal({
|
|
247
|
+
"@id": resourceId,
|
|
248
|
+
});
|
|
249
|
+
window.removeEventListener("requestNavigation", handler);
|
|
250
|
+
done();
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
window.addEventListener("requestNavigation", handler);
|
|
254
|
+
requestNavigation("/route", resourceId);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import uniq from "@helpers/utils/uniq";
|
|
2
|
+
|
|
3
|
+
describe("uniq helper function", () => {
|
|
4
|
+
describe("Basic functionality", () => {
|
|
5
|
+
it("should return a string", () => {
|
|
6
|
+
const result = uniq();
|
|
7
|
+
expect(result).to.be.a("string");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("should return a non-empty string", () => {
|
|
11
|
+
const result = uniq();
|
|
12
|
+
expect(result).to.not.equal("");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should return a value with hyphen separator", () => {
|
|
16
|
+
const result = uniq();
|
|
17
|
+
expect(result).to.contain("-");
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("Format validation", () => {
|
|
22
|
+
it("should have timestamp as prefix before hyphen", () => {
|
|
23
|
+
const result = uniq();
|
|
24
|
+
const parts = result.split("-");
|
|
25
|
+
expect(parts).to.have.lengthOf(2);
|
|
26
|
+
expect(parts[0]).to.match(/^\d+$/);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should have hex string as suffix after hyphen", () => {
|
|
30
|
+
const result = uniq();
|
|
31
|
+
const parts = result.split("-");
|
|
32
|
+
expect(parts).to.have.lengthOf(2);
|
|
33
|
+
expect(parts[1]).to.match(/^[0-9a-f]+$/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should use lowercase hex characters", () => {
|
|
37
|
+
const result = uniq();
|
|
38
|
+
const hexPart = result.split("-")[1];
|
|
39
|
+
expect(hexPart).to.match(/^[0-9a-f]+$/);
|
|
40
|
+
expect(hexPart).to.not.match(/[A-F]/);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("Uniqueness", () => {
|
|
45
|
+
it("should generate unique values on consecutive calls", () => {
|
|
46
|
+
const result1 = uniq();
|
|
47
|
+
const result2 = uniq();
|
|
48
|
+
expect(result1).to.not.equal(result2);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should generate unique values across multiple calls", () => {
|
|
52
|
+
const results = new Set<string>();
|
|
53
|
+
const callCount = 100;
|
|
54
|
+
|
|
55
|
+
for (let i = 0; i < callCount; i++) {
|
|
56
|
+
results.add(uniq());
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
expect(results.size).to.equal(callCount);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should generate unique values even when called rapidly", () => {
|
|
63
|
+
const results = new Set<string>();
|
|
64
|
+
const callCount = 50;
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < callCount; i++) {
|
|
67
|
+
results.add(uniq());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
expect(results.size).to.equal(callCount);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("Timestamp component", () => {
|
|
75
|
+
it("should use current timestamp", () => {
|
|
76
|
+
const before = Date.now();
|
|
77
|
+
const result = uniq();
|
|
78
|
+
const after = Date.now();
|
|
79
|
+
|
|
80
|
+
const timestamp = Number(result.split("-")[0]);
|
|
81
|
+
expect(timestamp).to.be.at.least(before);
|
|
82
|
+
expect(timestamp).to.be.at.most(after);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should have increasing timestamp over time", () => {
|
|
86
|
+
const result1 = uniq();
|
|
87
|
+
const timestamp1 = Number(result1.split("-")[0]);
|
|
88
|
+
|
|
89
|
+
setTimeout(() => {
|
|
90
|
+
const result2 = uniq();
|
|
91
|
+
const timestamp2 = Number(result2.split("-")[0]);
|
|
92
|
+
expect(timestamp2).to.be.greaterThan(timestamp1);
|
|
93
|
+
}, 10);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("Random hex component", () => {
|
|
98
|
+
it("should have reasonable hex string length", () => {
|
|
99
|
+
const result = uniq();
|
|
100
|
+
const hexPart = result.split("-")[1];
|
|
101
|
+
expect(hexPart.length).to.be.greaterThan(0);
|
|
102
|
+
expect(hexPart.length).to.be.lessThan(20);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("should vary in hex values", () => {
|
|
106
|
+
const results: string[] = [];
|
|
107
|
+
const callCount = 100;
|
|
108
|
+
|
|
109
|
+
for (let i = 0; i < callCount; i++) {
|
|
110
|
+
results.push(uniq());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const hexParts = results.map((r) => r.split("-")[1]);
|
|
114
|
+
const uniqueHexParts = new Set(hexParts);
|
|
115
|
+
expect(uniqueHexParts.size).to.be.greaterThan(50);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("Consistency", () => {
|
|
120
|
+
it("should always return string type", () => {
|
|
121
|
+
for (let i = 0; i < 10; i++) {
|
|
122
|
+
const result = uniq();
|
|
123
|
+
expect(typeof result).to.equal("string");
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should always maintain the same format", () => {
|
|
128
|
+
for (let i = 0; i < 10; i++) {
|
|
129
|
+
const result = uniq();
|
|
130
|
+
expect(result).to.match(/^\d+-[0-9a-f]+$/);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("Edge cases", () => {
|
|
136
|
+
it("should handle rapid successive calls", () => {
|
|
137
|
+
const results = [];
|
|
138
|
+
for (let i = 0; i < 1000; i++) {
|
|
139
|
+
results.push(uniq());
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const uniqueResults = new Set(results);
|
|
143
|
+
expect(uniqueResults.size).to.equal(1000);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should work when called from different contexts", () => {
|
|
147
|
+
const result1 = uniq();
|
|
148
|
+
const result2 = {
|
|
149
|
+
id: uniq(),
|
|
150
|
+
};
|
|
151
|
+
const result3 = [uniq()];
|
|
152
|
+
|
|
153
|
+
expect(result1).to.be.a("string");
|
|
154
|
+
expect(result2.id).to.be.a("string");
|
|
155
|
+
expect(result3[0]).to.be.a("string");
|
|
156
|
+
expect(result1).to.not.equal(result2.id);
|
|
157
|
+
expect(result2.id).to.not.equal(result3[0]);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
package/cypress.config.ts
CHANGED