wdio-ag-grid 2.0.0
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/CHANGELOG.md +18 -0
- package/README.md +84 -0
- package/docs/images/webdriverio-logo.png +0 -0
- package/package.json +41 -0
- package/src/index.d.ts +33 -0
- package/src/index.js +766 -0
- package/tests/ag-grid-animation-wait.v35.spec.js +65 -0
- package/tests/ag-grid-data.v33.spec.js +17 -0
- package/tests/ag-grid-data.v34.spec.js +17 -0
- package/tests/ag-grid-data.v35.spec.js +17 -0
- package/tests/ag-grid-elements.v33.spec.js +17 -0
- package/tests/ag-grid-elements.v34.spec.js +17 -0
- package/tests/ag-grid-elements.v35.spec.js +17 -0
- package/tests/server.mjs +48 -0
- package/tests/shared/compat.js +209 -0
- package/tests/shared/fixtures.js +109 -0
- package/tests/shared/run-ag-grid-data-suite.js +600 -0
- package/tests/shared/run-ag-grid-elements-suite.js +58 -0
- package/tests/shared/runtime.js +110 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { after } from "node:test";
|
|
2
|
+
|
|
3
|
+
import { createTestApi } from "./shared/compat.js";
|
|
4
|
+
import { createAgGrid } from "../src/index.js";
|
|
5
|
+
import { createWdioTestContext } from "./shared/runtime.js";
|
|
6
|
+
|
|
7
|
+
const context = await createWdioTestContext();
|
|
8
|
+
|
|
9
|
+
after(async () => {
|
|
10
|
+
await context.close();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const { expect, test } = createTestApi({
|
|
14
|
+
getBaseUrl: () => context.baseUrl,
|
|
15
|
+
getBrowser: () => context.browser,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test.describe("agGridWaitForAnimation", () => {
|
|
19
|
+
test("waits for AG Grid-owned animations to finish", async ({ page }) => {
|
|
20
|
+
await page.goto("/animation-wait/ag-owned.html");
|
|
21
|
+
await page.locator(".ag-cell").first().waitFor({ state: "visible" });
|
|
22
|
+
|
|
23
|
+
await page.evaluate(() => {
|
|
24
|
+
window.startAnimationWaitScenario();
|
|
25
|
+
window.__animationProbe.waitStartedAt = Date.now();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const grid = createAgGrid(await context.browser.$("#myGrid"));
|
|
29
|
+
await grid.waitForAnimation();
|
|
30
|
+
|
|
31
|
+
const probe = await page.evaluate(() => ({
|
|
32
|
+
...window.__animationProbe,
|
|
33
|
+
elapsedMs: Date.now() - window.__animationProbe.waitStartedAt,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
await expect(probe.agStarted).toBe(true);
|
|
37
|
+
await expect(probe.agFinished).toBe(true);
|
|
38
|
+
await expect(probe.elapsedMs).toBeGreaterThan(200);
|
|
39
|
+
await expect(probe.elapsedMs).toBeLessThan(2000);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("ignores third-party subtree animations whose finished promise never resolves", async ({ page }) => {
|
|
43
|
+
await page.goto("/animation-wait/third-party-subtree.html");
|
|
44
|
+
await page.locator(".ag-cell").first().waitFor({ state: "visible" });
|
|
45
|
+
|
|
46
|
+
await page.evaluate(() => {
|
|
47
|
+
window.startAnimationWaitScenario();
|
|
48
|
+
window.__animationProbe.waitStartedAt = Date.now();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
await expect(await page.locator("#myGrid .os-scrollbar-handle").count()).toBeGreaterThan(0);
|
|
52
|
+
|
|
53
|
+
const grid = createAgGrid(await context.browser.$("#myGrid"));
|
|
54
|
+
await grid.waitForAnimation();
|
|
55
|
+
|
|
56
|
+
const probe = await page.evaluate(() => ({
|
|
57
|
+
...window.__animationProbe,
|
|
58
|
+
elapsedMs: Date.now() - window.__animationProbe.waitStartedAt,
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
await expect(probe.agFinished).toBe(true);
|
|
62
|
+
await expect(probe.thirdPartyInstalled).toBe(true);
|
|
63
|
+
await expect(probe.elapsedMs).toBeLessThan(2000);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { after } from "node:test";
|
|
2
|
+
|
|
3
|
+
import { createWdioTestContext } from "./shared/runtime.js";
|
|
4
|
+
import { runAgGridDataSuite } from "./shared/run-ag-grid-data-suite.js";
|
|
5
|
+
|
|
6
|
+
const context = await createWdioTestContext();
|
|
7
|
+
|
|
8
|
+
after(async () => {
|
|
9
|
+
await context.close();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
runAgGridDataSuite({
|
|
13
|
+
getBaseUrl: () => context.baseUrl,
|
|
14
|
+
getBrowser: () => context.browser,
|
|
15
|
+
pagePath: "/v33/index.html",
|
|
16
|
+
versionLabel: "v33",
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { after } from "node:test";
|
|
2
|
+
|
|
3
|
+
import { createWdioTestContext } from "./shared/runtime.js";
|
|
4
|
+
import { runAgGridDataSuite } from "./shared/run-ag-grid-data-suite.js";
|
|
5
|
+
|
|
6
|
+
const context = await createWdioTestContext();
|
|
7
|
+
|
|
8
|
+
after(async () => {
|
|
9
|
+
await context.close();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
runAgGridDataSuite({
|
|
13
|
+
getBaseUrl: () => context.baseUrl,
|
|
14
|
+
getBrowser: () => context.browser,
|
|
15
|
+
pagePath: "/v34/index.html",
|
|
16
|
+
versionLabel: "v34",
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { after } from "node:test";
|
|
2
|
+
|
|
3
|
+
import { createWdioTestContext } from "./shared/runtime.js";
|
|
4
|
+
import { runAgGridDataSuite } from "./shared/run-ag-grid-data-suite.js";
|
|
5
|
+
|
|
6
|
+
const context = await createWdioTestContext();
|
|
7
|
+
|
|
8
|
+
after(async () => {
|
|
9
|
+
await context.close();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
runAgGridDataSuite({
|
|
13
|
+
getBaseUrl: () => context.baseUrl,
|
|
14
|
+
getBrowser: () => context.browser,
|
|
15
|
+
pagePath: "/index.html",
|
|
16
|
+
versionLabel: "v35",
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { after } from "node:test";
|
|
2
|
+
|
|
3
|
+
import { createWdioTestContext } from "./shared/runtime.js";
|
|
4
|
+
import { runAgGridElementsSuite } from "./shared/run-ag-grid-elements-suite.js";
|
|
5
|
+
|
|
6
|
+
const context = await createWdioTestContext();
|
|
7
|
+
|
|
8
|
+
after(async () => {
|
|
9
|
+
await context.close();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
runAgGridElementsSuite({
|
|
13
|
+
getBaseUrl: () => context.baseUrl,
|
|
14
|
+
getBrowser: () => context.browser,
|
|
15
|
+
pagePath: "/v33/index.html",
|
|
16
|
+
versionLabel: "v33",
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { after } from "node:test";
|
|
2
|
+
|
|
3
|
+
import { createWdioTestContext } from "./shared/runtime.js";
|
|
4
|
+
import { runAgGridElementsSuite } from "./shared/run-ag-grid-elements-suite.js";
|
|
5
|
+
|
|
6
|
+
const context = await createWdioTestContext();
|
|
7
|
+
|
|
8
|
+
after(async () => {
|
|
9
|
+
await context.close();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
runAgGridElementsSuite({
|
|
13
|
+
getBaseUrl: () => context.baseUrl,
|
|
14
|
+
getBrowser: () => context.browser,
|
|
15
|
+
pagePath: "/v34/index.html",
|
|
16
|
+
versionLabel: "v34",
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { after } from "node:test";
|
|
2
|
+
|
|
3
|
+
import { createWdioTestContext } from "./shared/runtime.js";
|
|
4
|
+
import { runAgGridElementsSuite } from "./shared/run-ag-grid-elements-suite.js";
|
|
5
|
+
|
|
6
|
+
const context = await createWdioTestContext();
|
|
7
|
+
|
|
8
|
+
after(async () => {
|
|
9
|
+
await context.close();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
runAgGridElementsSuite({
|
|
13
|
+
getBaseUrl: () => context.baseUrl,
|
|
14
|
+
getBrowser: () => context.browser,
|
|
15
|
+
pagePath: "/index.html",
|
|
16
|
+
versionLabel: "v35",
|
|
17
|
+
});
|
package/tests/server.mjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const appRoot = path.resolve(__dirname, "../../cypress-ag-grid/app");
|
|
9
|
+
const portArg = process.argv.find((value) => value.startsWith("--port="));
|
|
10
|
+
const port = portArg ? Number(portArg.split("=")[1]) : 4174;
|
|
11
|
+
|
|
12
|
+
const contentTypes = {
|
|
13
|
+
".css": "text/css; charset=utf-8",
|
|
14
|
+
".html": "text/html; charset=utf-8",
|
|
15
|
+
".js": "application/javascript; charset=utf-8",
|
|
16
|
+
".json": "application/json; charset=utf-8",
|
|
17
|
+
".png": "image/png",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
http
|
|
21
|
+
.createServer((req, res) => {
|
|
22
|
+
const requestPath = req.url === "/" ? "/index.html" : req.url;
|
|
23
|
+
const safePath = path.normalize(decodeURIComponent(requestPath)).replace(/^(\.\.[/\\])+/, "");
|
|
24
|
+
const filePath = path.join(appRoot, safePath);
|
|
25
|
+
|
|
26
|
+
if (!filePath.startsWith(appRoot)) {
|
|
27
|
+
res.writeHead(403);
|
|
28
|
+
res.end("Forbidden");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fs.readFile(filePath, (error, data) => {
|
|
33
|
+
if (error) {
|
|
34
|
+
res.writeHead(error.code === "ENOENT" ? 404 : 500);
|
|
35
|
+
res.end(error.code === "ENOENT" ? "Not found" : "Server error");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
res.writeHead(200, {
|
|
40
|
+
"Content-Type":
|
|
41
|
+
contentTypes[path.extname(filePath)] || "application/octet-stream",
|
|
42
|
+
});
|
|
43
|
+
res.end(data);
|
|
44
|
+
});
|
|
45
|
+
})
|
|
46
|
+
.listen(port, "127.0.0.1", () => {
|
|
47
|
+
console.log(`WDIO AG Grid test server listening on ${port}`);
|
|
48
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { beforeEach, describe, test as nodeTest } from "node:test";
|
|
3
|
+
|
|
4
|
+
class WdioLocator {
|
|
5
|
+
constructor(browser, resolver) {
|
|
6
|
+
this.browser = browser;
|
|
7
|
+
this.resolver = resolver;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async _elements() {
|
|
11
|
+
return this.resolver();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
locator(selector) {
|
|
15
|
+
return new WdioLocator(this.browser, async () => {
|
|
16
|
+
const elements = await this._elements();
|
|
17
|
+
const nested = [];
|
|
18
|
+
|
|
19
|
+
for (const element of elements) {
|
|
20
|
+
nested.push(...(await element.$$(selector)));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return nested;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
filter({ hasText }) {
|
|
28
|
+
return new WdioLocator(this.browser, async () => {
|
|
29
|
+
const elements = await this._elements();
|
|
30
|
+
const filtered = [];
|
|
31
|
+
|
|
32
|
+
for (const element of elements) {
|
|
33
|
+
const text = (await element.getText()).trim();
|
|
34
|
+
const matches =
|
|
35
|
+
hasText instanceof RegExp ? hasText.test(text) : text.includes(hasText);
|
|
36
|
+
|
|
37
|
+
if (matches) {
|
|
38
|
+
filtered.push(element);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return filtered;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
first() {
|
|
47
|
+
return this.nth(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
nth(index) {
|
|
51
|
+
return new WdioLocator(this.browser, async () => {
|
|
52
|
+
const elements = await this._elements();
|
|
53
|
+
return elements[index] ? [elements[index]] : [];
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async count() {
|
|
58
|
+
return (await this._elements()).length;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async waitFor({ state } = {}) {
|
|
62
|
+
const elements = await this._elements();
|
|
63
|
+
const element = elements[0];
|
|
64
|
+
|
|
65
|
+
if (!element) {
|
|
66
|
+
throw new Error("Unable to find locator element.");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (state === "visible") {
|
|
70
|
+
await element.waitForDisplayed();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await element.waitForExist();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async click() {
|
|
78
|
+
const elements = await this._elements();
|
|
79
|
+
await elements[0].click();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async dblclick() {
|
|
83
|
+
const elements = await this._elements();
|
|
84
|
+
await elements[0].doubleClick();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async fill(value) {
|
|
88
|
+
const elements = await this._elements();
|
|
89
|
+
await elements[0].setValue(value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async type(value) {
|
|
93
|
+
const elements = await this._elements();
|
|
94
|
+
await elements[0].addValue(value);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async press(key) {
|
|
98
|
+
await this.browser.keys(key);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async textContent() {
|
|
102
|
+
const elements = await this._elements();
|
|
103
|
+
const texts = [];
|
|
104
|
+
|
|
105
|
+
for (const element of elements) {
|
|
106
|
+
texts.push((await element.getText()).trim());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return texts.join(" ");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function createPage(browser, baseUrl) {
|
|
114
|
+
return {
|
|
115
|
+
async goto(path) {
|
|
116
|
+
const url = path.startsWith("http") ? path : `${baseUrl}${path}`;
|
|
117
|
+
await browser.url(url);
|
|
118
|
+
},
|
|
119
|
+
locator(selector) {
|
|
120
|
+
return new WdioLocator(browser, async () => browser.$$(selector));
|
|
121
|
+
},
|
|
122
|
+
async evaluate(fn, arg) {
|
|
123
|
+
return browser.execute(fn, arg);
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function createTestApi({ getBrowser, getBaseUrl }) {
|
|
129
|
+
const state = {
|
|
130
|
+
page: null,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const test = (name, fn) =>
|
|
134
|
+
nodeTest(name, async () => {
|
|
135
|
+
if (!state.page) {
|
|
136
|
+
state.page = createPage(getBrowser(), getBaseUrl());
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await fn({ page: state.page });
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test.describe = describe;
|
|
143
|
+
test.beforeEach = (fn) =>
|
|
144
|
+
beforeEach(async () => {
|
|
145
|
+
state.page = createPage(getBrowser(), getBaseUrl());
|
|
146
|
+
await fn({ page: state.page });
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const expect = (actual) => {
|
|
150
|
+
const isLocator = actual instanceof WdioLocator;
|
|
151
|
+
|
|
152
|
+
const api = {
|
|
153
|
+
toEqual(expected) {
|
|
154
|
+
assert.deepStrictEqual(actual, expected);
|
|
155
|
+
},
|
|
156
|
+
toBe(expected) {
|
|
157
|
+
assert.strictEqual(actual, expected);
|
|
158
|
+
},
|
|
159
|
+
toContain(expected) {
|
|
160
|
+
if (typeof actual === "string") {
|
|
161
|
+
assert.ok(actual.includes(expected));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
assert.ok(actual.includes(expected));
|
|
166
|
+
},
|
|
167
|
+
toContainEqual(expected) {
|
|
168
|
+
assert.ok(
|
|
169
|
+
actual.some((item) => JSON.stringify(item) === JSON.stringify(expected)),
|
|
170
|
+
`Expected ${JSON.stringify(expected)} to be present.`
|
|
171
|
+
);
|
|
172
|
+
},
|
|
173
|
+
toBeGreaterThan(expected) {
|
|
174
|
+
assert.ok(actual > expected);
|
|
175
|
+
},
|
|
176
|
+
toBeLessThan(expected) {
|
|
177
|
+
assert.ok(actual < expected);
|
|
178
|
+
},
|
|
179
|
+
toContainText(expected) {
|
|
180
|
+
if (isLocator) {
|
|
181
|
+
return actual.textContent().then((text) => {
|
|
182
|
+
assert.ok(text.includes(expected), `Expected "${text}" to include "${expected}"`);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const text = String(actual);
|
|
187
|
+
assert.ok(text.includes(expected), `Expected "${text}" to include "${expected}"`);
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
api.not = {
|
|
192
|
+
toContain(expected) {
|
|
193
|
+
if (typeof actual === "string") {
|
|
194
|
+
assert.ok(!actual.includes(expected));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
assert.ok(!actual.includes(expected));
|
|
199
|
+
},
|
|
200
|
+
toBe(expected) {
|
|
201
|
+
assert.notStrictEqual(actual, expected);
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
return api;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
return { expect, test };
|
|
209
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
const cardataPath = path.resolve(
|
|
8
|
+
__dirname,
|
|
9
|
+
"../../../cypress-ag-grid/cypress/fixtures/cardata.json"
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
export const agGridSelector = "#myGrid";
|
|
13
|
+
export const agGridElementsSelector = "#myGrid2";
|
|
14
|
+
|
|
15
|
+
export const pageSize = 5;
|
|
16
|
+
|
|
17
|
+
export const cardataFixture = JSON.parse(
|
|
18
|
+
fs.readFileSync(cardataPath, "utf-8")
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const expectedPaginatedTableData = [
|
|
22
|
+
[
|
|
23
|
+
{ Year: "2020", Make: "Toyota", Model: "Celica", Condition: "fair", Price: "35000" },
|
|
24
|
+
{ Year: "2020", Make: "Ford", Model: "Mondeo", Condition: "excellent", Price: "32000" },
|
|
25
|
+
{ Year: "2020", Make: "Porsche", Model: "Boxter", Condition: "good", Price: "72000" },
|
|
26
|
+
{ Year: "2020", Make: "BMW", Model: "3-series", Condition: "fair", Price: "45000" },
|
|
27
|
+
{ Year: "2020", Make: "Mercedes", Model: "GLC300", Condition: "good", Price: "53000" },
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
{ Year: "2020", Make: "Honda", Model: "Civic", Condition: "poor", Price: "22000" },
|
|
31
|
+
{ Year: "2020", Make: "Honda", Model: "Accord", Condition: "poor", Price: "32000" },
|
|
32
|
+
{ Year: "2020", Make: "Ford", Model: "Taurus", Condition: "excellent", Price: "19000" },
|
|
33
|
+
{ Year: "2020", Make: "Hyundai", Model: "Elantra", Condition: "good", Price: "22000" },
|
|
34
|
+
{ Year: "2020", Make: "Toyota", Model: "Celica", Condition: "poor", Price: "5000" },
|
|
35
|
+
],
|
|
36
|
+
[
|
|
37
|
+
{ Year: "2020", Make: "Ford", Model: "Mondeo", Condition: "good", Price: "25000" },
|
|
38
|
+
{ Year: "2020", Make: "Porsche", Model: "Boxter", Condition: "good", Price: "99000" },
|
|
39
|
+
{ Year: "2020", Make: "BMW", Model: "3-series", Condition: "poor", Price: "32000" },
|
|
40
|
+
{ Year: "2020", Make: "Mercedes", Model: "GLC300", Condition: "excellent", Price: "35000" },
|
|
41
|
+
{ Year: "2011", Make: "Honda", Model: "Civic", Condition: "good", Price: "9000" },
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
{ Year: "2020", Make: "Honda", Model: "Accord", Condition: "good", Price: "34000" },
|
|
45
|
+
{ Year: "1990", Make: "Ford", Model: "Taurus", Condition: "excellent", Price: "900" },
|
|
46
|
+
{ Year: "2020", Make: "Hyundai", Model: "Elantra", Condition: "fair", Price: "3000" },
|
|
47
|
+
{ Year: "2020", Make: "BMW", Model: "2002", Condition: "excellent", Price: "88001" },
|
|
48
|
+
{ Year: "2023", Make: "Hyundai", Model: "Santa Fe", Condition: "excellent", Price: "" },
|
|
49
|
+
],
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export const expectedFirstPageTableData = expectedPaginatedTableData[0];
|
|
53
|
+
|
|
54
|
+
export const expectedPorscheRowsBeforeEditing = [
|
|
55
|
+
{ Year: "2020", Make: "Porsche", Model: "Boxter", Price: "72000" },
|
|
56
|
+
{ Year: "2020", Make: "Porsche", Model: "Boxter", Price: "99000" },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
export const expectedPorscheRowsAfterEditing = [
|
|
60
|
+
{ Year: "2020", Make: "Porsche", Model: "Boxter", Price: "66000" },
|
|
61
|
+
{ Year: "2020", Make: "Porsche", Model: "Boxter", Price: "99000" },
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
export function clone(value) {
|
|
65
|
+
return structuredClone(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function removePropertyFromCollection(collection, columnsToExclude) {
|
|
69
|
+
const cloned = clone(collection);
|
|
70
|
+
|
|
71
|
+
if (!columnsToExclude) {
|
|
72
|
+
return cloned;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const excludedColumn of columnsToExclude) {
|
|
76
|
+
for (const row of cloned) {
|
|
77
|
+
delete row[excludedColumn];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return cloned;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function sortedCollectionByProperty(collection, columnName, sortedBy, pageSizeLimit = pageSize) {
|
|
85
|
+
const cloned = clone(collection);
|
|
86
|
+
const direction = sortedBy === "desc" ? -1 : 1;
|
|
87
|
+
|
|
88
|
+
return cloned
|
|
89
|
+
.sort((a, b) => {
|
|
90
|
+
const valueA = String(a[columnName] ?? "").toUpperCase();
|
|
91
|
+
const valueB = String(b[columnName] ?? "").toUpperCase();
|
|
92
|
+
if (valueA < valueB) return -1 * direction;
|
|
93
|
+
if (valueA > valueB) return 1 * direction;
|
|
94
|
+
return 0;
|
|
95
|
+
})
|
|
96
|
+
.slice(0, pageSizeLimit);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function getSortedMileage(actualTableData) {
|
|
100
|
+
return actualTableData
|
|
101
|
+
.map((row) => row.Mileage)
|
|
102
|
+
.sort((a, b) => Number(a) - Number(b));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function expectRowsSubset(expect, actualRows, expectedRows) {
|
|
106
|
+
for (const expectedRow of expectedRows) {
|
|
107
|
+
expect(actualRows).toContainEqual(expectedRow);
|
|
108
|
+
}
|
|
109
|
+
}
|