terra-draw 0.0.1-alpha.6 → 0.0.1-alpha.61
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/.devcontainer/Dockerfile +8 -0
- package/.devcontainer/devcontainer.json +21 -0
- package/.devcontainer/post-create.sh +16 -0
- package/README.md +30 -6
- package/dist/adapters/arcgis-maps-sdk.adapter.d.ts +105 -0
- package/dist/adapters/common/adapter-listener.d.ts +22 -0
- package/dist/adapters/common/base.adapter.d.ts +59 -0
- package/dist/adapters/google-maps.adapter.d.ts +90 -39
- package/dist/adapters/leaflet.adapter.d.ts +97 -33
- package/dist/adapters/mapbox-gl.adapter.d.ts +89 -37
- package/dist/adapters/maplibre-gl.adapter.d.ts +71 -0
- package/dist/adapters/openlayers.adapter.d.ts +118 -0
- package/dist/common.d.ts +103 -98
- package/dist/geometry/boolean/is-valid-coordinate.d.ts +4 -0
- package/dist/geometry/boolean/is-valid-linestring-feature.d.ts +2 -0
- package/dist/geometry/boolean/is-valid-point.d.ts +2 -0
- package/dist/geometry/boolean/is-valid-polygon-feature.d.ts +3 -0
- package/dist/geometry/boolean/point-in-polygon.d.ts +2 -2
- package/dist/geometry/boolean/self-intersects.d.ts +2 -2
- package/dist/geometry/centroid.d.ts +2 -2
- package/dist/geometry/coordinates-identical.d.ts +2 -2
- package/dist/geometry/get-coordinates-as-points.d.ts +6 -6
- package/dist/geometry/get-midpoints.d.ts +8 -7
- package/dist/geometry/helpers.d.ts +4 -4
- package/dist/geometry/limit-decimal-precision.d.ts +1 -1
- package/dist/geometry/measure/haversine-distance.d.ts +2 -2
- package/dist/geometry/measure/pixel-distance-to-line.d.ts +10 -10
- package/dist/geometry/measure/pixel-distance.d.ts +7 -7
- package/dist/geometry/measure/rhumb-bearing.d.ts +2 -2
- package/dist/geometry/measure/rhumb-destination.d.ts +2 -2
- package/dist/geometry/measure/rhumb-distance.d.ts +2 -2
- package/dist/geometry/midpoint-coordinate.d.ts +3 -2
- package/dist/geometry/shape/create-bbox.d.ts +10 -0
- package/dist/geometry/shape/create-circle.d.ts +7 -7
- package/dist/geometry/shape/great-circle-line.d.ts +12 -0
- package/dist/geometry/transform/rotate.d.ts +2 -2
- package/dist/geometry/transform/scale.d.ts +2 -2
- package/dist/modes/base.behavior.d.ts +19 -19
- package/dist/modes/base.mode.d.ts +62 -33
- package/dist/modes/circle/circle.mode.d.ts +55 -35
- package/dist/modes/click-bounding-box.behavior.d.ts +6 -7
- package/dist/modes/freehand/freehand.mode.d.ts +63 -37
- package/dist/modes/great-circle-snapping.behavior.d.ts +14 -0
- package/dist/modes/greatcircle/great-circle.mode.d.ts +64 -0
- package/dist/modes/linestring/linestring.mode.d.ts +67 -40
- package/dist/modes/pixel-distance.behavior.d.ts +7 -7
- package/dist/modes/point/point.mode.d.ts +44 -26
- package/dist/modes/polygon/behaviors/closing-points.behavior.d.ts +19 -19
- package/dist/modes/polygon/polygon.mode.d.ts +70 -49
- package/dist/modes/rectangle/rectangle.mode.d.ts +55 -0
- package/dist/modes/render/render.mode.d.ts +52 -23
- package/dist/modes/select/behaviors/drag-coordinate-resize.behavior.d.ts +28 -0
- package/dist/modes/select/behaviors/drag-coordinate.behavior.d.ts +20 -13
- package/dist/modes/select/behaviors/drag-feature.behavior.d.ts +20 -17
- package/dist/modes/select/behaviors/{features-at-mouse-event.behavior.d.ts → feature-at-pointer-event.behavior.d.ts} +15 -15
- package/dist/modes/select/behaviors/midpoint.behavior.d.ts +19 -18
- package/dist/modes/select/behaviors/rotate-feature.behavior.d.ts +14 -13
- package/dist/modes/select/behaviors/scale-feature.behavior.d.ts +14 -13
- package/dist/modes/select/behaviors/selection-point.behavior.d.ts +19 -18
- package/dist/modes/select/select.mode.d.ts +117 -78
- package/dist/modes/snapping.behavior.d.ts +16 -13
- package/dist/modes/static/static.mode.d.ts +32 -30
- package/dist/store/spatial-index/quickselect.d.ts +2 -2
- package/dist/store/spatial-index/rbush.d.ts +35 -35
- package/dist/store/spatial-index/spatial-index.d.ts +18 -18
- package/dist/store/store-feature-validation.d.ts +16 -0
- package/dist/store/store.d.ts +59 -48
- package/dist/terra-draw.cjs +1 -1
- package/dist/terra-draw.cjs.map +1 -1
- package/dist/terra-draw.d.ts +220 -48
- package/dist/terra-draw.modern.js +1 -1
- package/dist/terra-draw.modern.js.map +1 -1
- package/dist/terra-draw.module.js +1 -1
- package/dist/terra-draw.module.js.map +1 -1
- package/dist/terra-draw.umd.js +1 -1
- package/dist/terra-draw.umd.js.map +1 -1
- package/dist/util/geoms.d.ts +3 -3
- package/dist/util/id.d.ts +1 -1
- package/dist/util/styling.d.ts +2 -2
- package/e2e/README.md +29 -0
- package/e2e/package-lock.json +4249 -0
- package/e2e/package.json +26 -0
- package/e2e/playwright.config.ts +77 -0
- package/e2e/public/index.html +53 -0
- package/e2e/src/index.ts +165 -0
- package/e2e/tests/leaflet.spec.ts +413 -0
- package/e2e/tests/setup.ts +164 -0
- package/e2e/webpack.config.js +36 -0
- package/jest.nocheck.config.ts +17 -0
- package/package.json +157 -87
- package/readme.gif +0 -0
- package/tsconfig.json +17 -12
- package/CODE_OF_CONDUCT.md +0 -36
- package/CONTRIBUTING.md +0 -17
- package/DEVELOPMENT.md +0 -77
- package/dist/bundle.js +0 -6
- package/dist/bundle.js.LICENSE.txt +0 -4
- package/dist/geometry/create-circle.d.ts +0 -6
- package/dist/geometry/get-pixel-distance-to-line.d.ts +0 -10
- package/dist/geometry/get-pixel-distance.d.ts +0 -7
- package/dist/geometry/haversine-distance.d.ts +0 -1
- package/dist/geometry/point-in-polygon.d.ts +0 -1
- package/dist/geometry/self-intersects.d.ts +0 -2
- package/dist/modes/circle.mode.d.ts +0 -18
- package/dist/modes/freehand.mode.d.ts +0 -20
- package/dist/modes/line-string.mode.d.ts +0 -21
- package/dist/modes/point.mode.d.ts +0 -14
- package/dist/modes/polygon/behaviors/start-end-point.behavior.d.ts +0 -11
- package/dist/modes/polygon.mode.d.ts +0 -21
- package/dist/modes/select.mode.d.ts +0 -21
- package/dist/modes/static.mode.d.ts +0 -10
- package/jest.config.ts +0 -27
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
import {
|
|
3
|
+
changeMode,
|
|
4
|
+
drawTwoClickShape,
|
|
5
|
+
drawRectangularPolygon,
|
|
6
|
+
expectGroupPosition,
|
|
7
|
+
expectPathDimensions,
|
|
8
|
+
expectPaths,
|
|
9
|
+
pageUrl,
|
|
10
|
+
setupMap,
|
|
11
|
+
} from "./setup";
|
|
12
|
+
|
|
13
|
+
test.describe("page setup", () => {
|
|
14
|
+
test("loads map", async ({ page }) => {
|
|
15
|
+
await page.goto(pageUrl);
|
|
16
|
+
|
|
17
|
+
// Expect map application to exist
|
|
18
|
+
await expect(page.getByRole("application")).toBeVisible();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("loads UI", async ({ page }) => {
|
|
22
|
+
await page.goto(pageUrl);
|
|
23
|
+
|
|
24
|
+
await expect(page.getByText("Point")).toBeVisible();
|
|
25
|
+
await expect(page.getByText("Linestring")).toBeVisible();
|
|
26
|
+
await expect(page.getByText("Polygon")).toBeVisible();
|
|
27
|
+
await expect(page.getByText("Select")).toBeVisible();
|
|
28
|
+
await expect(page.getByText("Clear")).toBeVisible();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("there are no console errors", async ({ page }) => {
|
|
32
|
+
const errors: string[] = [];
|
|
33
|
+
page.on("console", (msg) => {
|
|
34
|
+
if (msg.type() === "error") {
|
|
35
|
+
errors.push(msg.text());
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
await page.goto(pageUrl);
|
|
39
|
+
await expect(page.getByRole("application")).toBeVisible();
|
|
40
|
+
|
|
41
|
+
expect(errors).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("there are no build issues", async ({ page }) => {
|
|
45
|
+
await page.goto(pageUrl);
|
|
46
|
+
await expect(
|
|
47
|
+
await page.locator("#webpack-dev-server-client-overlay").count(),
|
|
48
|
+
).toBe(0);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test.describe("point mode", () => {
|
|
53
|
+
const mode = "point";
|
|
54
|
+
|
|
55
|
+
test("mode can set and can be used to create a point", async ({ page }) => {
|
|
56
|
+
const mapDiv = await setupMap({ page });
|
|
57
|
+
await changeMode({ page, mode });
|
|
58
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
59
|
+
|
|
60
|
+
await expectPaths({ page, count: 1 });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("mode can set and can be used to create multiple points", async ({
|
|
64
|
+
page,
|
|
65
|
+
}) => {
|
|
66
|
+
const mapDiv = await setupMap({ page });
|
|
67
|
+
await changeMode({ page, mode });
|
|
68
|
+
|
|
69
|
+
await page.mouse.click(mapDiv.width / 4, mapDiv.height / 4);
|
|
70
|
+
await page.mouse.click(mapDiv.width / 3, mapDiv.height / 3);
|
|
71
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
72
|
+
|
|
73
|
+
await expectPaths({ page, count: 3 });
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test.describe("linestring mode", () => {
|
|
78
|
+
const mode = "linestring";
|
|
79
|
+
|
|
80
|
+
test("mode can set and can be used to create a linestring", async ({
|
|
81
|
+
page,
|
|
82
|
+
}) => {
|
|
83
|
+
const mapDiv = await setupMap({ page });
|
|
84
|
+
await changeMode({ page, mode });
|
|
85
|
+
|
|
86
|
+
await page.mouse.move(mapDiv.width / 2, mapDiv.height / 2);
|
|
87
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
88
|
+
await page.mouse.move(mapDiv.width / 3, mapDiv.height / 3);
|
|
89
|
+
await page.mouse.click(mapDiv.width / 3, mapDiv.height / 3);
|
|
90
|
+
|
|
91
|
+
await page.mouse.click(mapDiv.width / 3, mapDiv.height / 3);
|
|
92
|
+
|
|
93
|
+
await expectPaths({ page, count: 1 });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("mode can set and can be used to create multiple linestrings", async ({
|
|
97
|
+
page,
|
|
98
|
+
}) => {
|
|
99
|
+
const mapDiv = await setupMap({ page });
|
|
100
|
+
await changeMode({ page, mode });
|
|
101
|
+
|
|
102
|
+
// First line
|
|
103
|
+
await page.mouse.move(mapDiv.width / 2, mapDiv.height / 2);
|
|
104
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
105
|
+
await page.mouse.move(mapDiv.width / 3, mapDiv.height / 3);
|
|
106
|
+
await page.mouse.click(mapDiv.width / 3, mapDiv.height / 3);
|
|
107
|
+
|
|
108
|
+
// One point + one line
|
|
109
|
+
await expectPaths({ page, count: 2 });
|
|
110
|
+
|
|
111
|
+
// Close first line
|
|
112
|
+
await page.mouse.click(mapDiv.width / 3, mapDiv.height / 3);
|
|
113
|
+
|
|
114
|
+
// One line
|
|
115
|
+
await expectPaths({ page, count: 1 });
|
|
116
|
+
|
|
117
|
+
// Second line
|
|
118
|
+
await page.mouse.move(mapDiv.width / 4, mapDiv.height / 4);
|
|
119
|
+
await page.mouse.click(mapDiv.width / 4, mapDiv.height / 4);
|
|
120
|
+
await page.mouse.move(mapDiv.width / 5, mapDiv.height / 5);
|
|
121
|
+
await page.mouse.click(mapDiv.width / 5, mapDiv.height / 5);
|
|
122
|
+
|
|
123
|
+
// Close second line
|
|
124
|
+
await page.mouse.click(mapDiv.width / 5, mapDiv.height / 5);
|
|
125
|
+
|
|
126
|
+
// Two lines
|
|
127
|
+
await expectPaths({ page, count: 2 });
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test.describe("polygon mode", () => {
|
|
132
|
+
const mode = "polygon";
|
|
133
|
+
|
|
134
|
+
test("mode can set and can be used to create a polygon", async ({ page }) => {
|
|
135
|
+
const mapDiv = await setupMap({ page });
|
|
136
|
+
await changeMode({ page, mode });
|
|
137
|
+
|
|
138
|
+
// The length of the square sides in pixels
|
|
139
|
+
const sideLength = 100;
|
|
140
|
+
|
|
141
|
+
// Calculating the half of the side length
|
|
142
|
+
const halfLength = sideLength / 2;
|
|
143
|
+
|
|
144
|
+
// Coordinates of the center
|
|
145
|
+
const centerX = mapDiv.width / 2;
|
|
146
|
+
const centerY = mapDiv.height / 2;
|
|
147
|
+
|
|
148
|
+
// Coordinates of the four corners of the square
|
|
149
|
+
const topLeft = { x: centerX - halfLength, y: centerY - halfLength };
|
|
150
|
+
const topRight = { x: centerX + halfLength, y: centerY - halfLength };
|
|
151
|
+
const bottomLeft = { x: centerX - halfLength, y: centerY + halfLength };
|
|
152
|
+
const bottomRight = { x: centerX + halfLength, y: centerY + halfLength };
|
|
153
|
+
|
|
154
|
+
// Perform clicks at each corner
|
|
155
|
+
await page.mouse.click(topLeft.x, topLeft.y);
|
|
156
|
+
await page.mouse.click(topRight.x, topRight.y);
|
|
157
|
+
await page.mouse.click(bottomRight.x, bottomRight.y);
|
|
158
|
+
await page.mouse.click(bottomLeft.x, bottomLeft.y);
|
|
159
|
+
|
|
160
|
+
// Close the square
|
|
161
|
+
await page.mouse.click(bottomLeft.x, bottomLeft.y);
|
|
162
|
+
|
|
163
|
+
// One point + one line
|
|
164
|
+
await expectPaths({ page, count: 1 });
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test.describe("rectangle mode", () => {
|
|
169
|
+
const mode = "rectangle";
|
|
170
|
+
|
|
171
|
+
test("mode can set and can be used to create a rectangle", async ({
|
|
172
|
+
page,
|
|
173
|
+
}) => {
|
|
174
|
+
const mapDiv = await setupMap({ page });
|
|
175
|
+
await changeMode({ page, mode });
|
|
176
|
+
|
|
177
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
178
|
+
await page.mouse.click(mapDiv.width / 2 + 50, mapDiv.height / 2 + 50);
|
|
179
|
+
|
|
180
|
+
// One point + one line
|
|
181
|
+
await expectPaths({ page, count: 1 });
|
|
182
|
+
|
|
183
|
+
await expectPathDimensions({ page, width: 54, height: 54 }); // Stroke width of 4
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test.describe("circle mode", () => {
|
|
188
|
+
const mode = "circle";
|
|
189
|
+
|
|
190
|
+
test("mode can set and can be used to create a circle", async ({ page }) => {
|
|
191
|
+
const mapDiv = await setupMap({ page });
|
|
192
|
+
await changeMode({ page, mode });
|
|
193
|
+
|
|
194
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
195
|
+
await page.mouse.click(mapDiv.width / 2 + 50, mapDiv.height / 2 + 50);
|
|
196
|
+
|
|
197
|
+
// One point + one line
|
|
198
|
+
await expectPaths({ page, count: 1 });
|
|
199
|
+
|
|
200
|
+
await expectPathDimensions({ page, width: 146, height: 146 });
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test.describe("greatcircle mode", () => {
|
|
205
|
+
const mode = "greatcircle";
|
|
206
|
+
|
|
207
|
+
test("mode can set and can be used to create a greatcircle", async ({
|
|
208
|
+
page,
|
|
209
|
+
}) => {
|
|
210
|
+
const mapDiv = await setupMap({ page });
|
|
211
|
+
await changeMode({ page, mode });
|
|
212
|
+
|
|
213
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
214
|
+
await page.mouse.click(mapDiv.width / 2 + 50, mapDiv.height / 2 + 50);
|
|
215
|
+
|
|
216
|
+
// One point + one line
|
|
217
|
+
await expectPaths({ page, count: 1 });
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test.describe("select mode", () => {
|
|
222
|
+
const mode = "select";
|
|
223
|
+
|
|
224
|
+
test("mode can set and then polygon can be selected and deselected", async ({
|
|
225
|
+
page,
|
|
226
|
+
}) => {
|
|
227
|
+
const mapDiv = await setupMap({ page });
|
|
228
|
+
|
|
229
|
+
await changeMode({ page, mode: "polygon" });
|
|
230
|
+
const sideLength = 100;
|
|
231
|
+
const halfLength = sideLength / 2;
|
|
232
|
+
const centerX = mapDiv.width / 2;
|
|
233
|
+
const centerY = mapDiv.height / 2;
|
|
234
|
+
const topLeft = { x: centerX - halfLength, y: centerY - halfLength };
|
|
235
|
+
const topRight = { x: centerX + halfLength, y: centerY - halfLength };
|
|
236
|
+
const bottomLeft = { x: centerX - halfLength, y: centerY + halfLength };
|
|
237
|
+
const bottomRight = { x: centerX + halfLength, y: centerY + halfLength };
|
|
238
|
+
await page.mouse.click(topLeft.x, topLeft.y);
|
|
239
|
+
await page.mouse.click(topRight.x, topRight.y);
|
|
240
|
+
await page.mouse.click(bottomRight.x, bottomRight.y);
|
|
241
|
+
await page.mouse.click(bottomLeft.x, bottomLeft.y);
|
|
242
|
+
await page.mouse.click(bottomLeft.x, bottomLeft.y); // Closed
|
|
243
|
+
|
|
244
|
+
await changeMode({ page, mode });
|
|
245
|
+
|
|
246
|
+
// Select
|
|
247
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
248
|
+
await expectPaths({ page, count: 9 }); // 8 selection points and 1 square
|
|
249
|
+
|
|
250
|
+
// Deselect
|
|
251
|
+
await page.mouse.click(mapDiv.width - 10, mapDiv.height / 2);
|
|
252
|
+
await expectPaths({ page, count: 1 }); // 0 selection points and 1 square
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("selected polygon can be dragged", async ({ page }) => {
|
|
256
|
+
const mapDiv = await setupMap({ page });
|
|
257
|
+
|
|
258
|
+
await changeMode({ page, mode: "polygon" });
|
|
259
|
+
|
|
260
|
+
// Draw a rectangle
|
|
261
|
+
const { topLeft } = await drawRectangularPolygon({ mapDiv, page });
|
|
262
|
+
|
|
263
|
+
// Change to select mode
|
|
264
|
+
await changeMode({ page, mode });
|
|
265
|
+
|
|
266
|
+
// Before drag
|
|
267
|
+
const x = topLeft.x - 2;
|
|
268
|
+
const y = topLeft.y - 2;
|
|
269
|
+
await expectGroupPosition({ page, x, y });
|
|
270
|
+
|
|
271
|
+
// Select
|
|
272
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
273
|
+
await expectPaths({ page, count: 9 }); // 8 selection points and 1 square
|
|
274
|
+
|
|
275
|
+
// Drag
|
|
276
|
+
await page.mouse.move(mapDiv.width / 2, mapDiv.height / 2);
|
|
277
|
+
await page.mouse.down();
|
|
278
|
+
await page.mouse.move(mapDiv.width / 2 + 50, mapDiv.height / 2 + 50, {
|
|
279
|
+
steps: 30,
|
|
280
|
+
}); // Steps is required
|
|
281
|
+
await page.mouse.up();
|
|
282
|
+
|
|
283
|
+
await page.mouse.click(mapDiv.width - 10, mapDiv.height / 2);
|
|
284
|
+
|
|
285
|
+
await expectGroupPosition({ page, x: x + 48, y: y + 48 });
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("selected polygon can have individual coordinates dragged", async ({
|
|
289
|
+
page,
|
|
290
|
+
}) => {
|
|
291
|
+
const mapDiv = await setupMap({ page });
|
|
292
|
+
|
|
293
|
+
await changeMode({ page, mode: "polygon" });
|
|
294
|
+
|
|
295
|
+
// Draw a rectangle
|
|
296
|
+
const { topLeft } = await drawRectangularPolygon({ mapDiv, page });
|
|
297
|
+
|
|
298
|
+
// Change to select mode
|
|
299
|
+
await changeMode({ page, mode });
|
|
300
|
+
|
|
301
|
+
// Before drag
|
|
302
|
+
const x = topLeft.x - 2;
|
|
303
|
+
const y = topLeft.y - 2;
|
|
304
|
+
await expectGroupPosition({ page, x, y });
|
|
305
|
+
|
|
306
|
+
// Select
|
|
307
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
308
|
+
await expectPaths({ page, count: 9 }); // 8 selection points and 1 square
|
|
309
|
+
|
|
310
|
+
// Drag
|
|
311
|
+
await page.mouse.move(topLeft.x, topLeft.y);
|
|
312
|
+
await page.mouse.down();
|
|
313
|
+
await page.mouse.move(topLeft.x - 50, topLeft.y + 50, { steps: 30 }); // Steps is required
|
|
314
|
+
await page.mouse.up();
|
|
315
|
+
|
|
316
|
+
// Deselect
|
|
317
|
+
await page.mouse.click(mapDiv.width - 10, mapDiv.height / 2);
|
|
318
|
+
|
|
319
|
+
// Dragged the coordinate to the left and down slightly
|
|
320
|
+
await expectGroupPosition({ page, x: 538, y: 308 });
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test("selected rectangle can has it's shape maintained when coordinates are dragged", async ({
|
|
324
|
+
page,
|
|
325
|
+
}) => {
|
|
326
|
+
const mapDiv = await setupMap({ page });
|
|
327
|
+
|
|
328
|
+
await changeMode({ page, mode: "rectangle" });
|
|
329
|
+
|
|
330
|
+
// Draw a rectangle
|
|
331
|
+
const { topLeft } = await drawTwoClickShape({ mapDiv, page });
|
|
332
|
+
|
|
333
|
+
// Change to select mode
|
|
334
|
+
await changeMode({ page, mode });
|
|
335
|
+
|
|
336
|
+
// Before drag
|
|
337
|
+
const x = topLeft.x - 2;
|
|
338
|
+
const y = topLeft.y - 2;
|
|
339
|
+
await expectGroupPosition({ page, x, y });
|
|
340
|
+
|
|
341
|
+
// Select
|
|
342
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
343
|
+
await expectPaths({ page, count: 5 }); // 4 selection points and 1 square
|
|
344
|
+
|
|
345
|
+
// Drag
|
|
346
|
+
await page.mouse.move(topLeft.x, topLeft.y);
|
|
347
|
+
await page.mouse.down();
|
|
348
|
+
await page.mouse.move(topLeft.x - 100, topLeft.y + 100, { steps: 50 }); // Steps is required
|
|
349
|
+
await page.mouse.up();
|
|
350
|
+
|
|
351
|
+
// Deselect
|
|
352
|
+
await page.mouse.click(mapDiv.width - 10, mapDiv.height / 2);
|
|
353
|
+
|
|
354
|
+
// Dragged the square up and to the left
|
|
355
|
+
await expectGroupPosition({ page, x: 584, y: 304 });
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test("selected circle can has it's shape maintained from center origin when coordinates are dragged", async ({
|
|
359
|
+
page,
|
|
360
|
+
}) => {
|
|
361
|
+
const mapDiv = await setupMap({ page });
|
|
362
|
+
|
|
363
|
+
await changeMode({ page, mode: "circle" });
|
|
364
|
+
|
|
365
|
+
// Draw a circle
|
|
366
|
+
await drawTwoClickShape({ mapDiv, page });
|
|
367
|
+
|
|
368
|
+
// Change to select mode
|
|
369
|
+
await changeMode({ page, mode });
|
|
370
|
+
|
|
371
|
+
// Select
|
|
372
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
373
|
+
await expectPaths({ page, count: 65 }); // 4 selection points and 1 square
|
|
374
|
+
|
|
375
|
+
// Drag
|
|
376
|
+
await page.mouse.move(mapDiv.width / 2, mapDiv.height / 2 + 50);
|
|
377
|
+
await page.mouse.down();
|
|
378
|
+
await page.mouse.move(mapDiv.width / 2, mapDiv.height / 2 + 100, {
|
|
379
|
+
steps: 50,
|
|
380
|
+
}); // Steps is required
|
|
381
|
+
await page.mouse.up();
|
|
382
|
+
|
|
383
|
+
// Deselect
|
|
384
|
+
await page.mouse.click(mapDiv.width - 10, mapDiv.height / 2);
|
|
385
|
+
|
|
386
|
+
// Dragged the square up and to the left
|
|
387
|
+
await expectGroupPosition({ page, x: 430, y: 150 });
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test.describe("clear", () => {
|
|
392
|
+
test("drawn geometries can be cleared correctly", async ({ page }) => {
|
|
393
|
+
const mapDiv = await setupMap({ page });
|
|
394
|
+
|
|
395
|
+
await changeMode({ page, mode: "point" });
|
|
396
|
+
await page.mouse.click(mapDiv.width / 4, mapDiv.height / 4);
|
|
397
|
+
|
|
398
|
+
await changeMode({ page, mode: "linestring" });
|
|
399
|
+
await page.mouse.click(mapDiv.width / 2, mapDiv.height / 2);
|
|
400
|
+
await page.mouse.click(mapDiv.width / 3, mapDiv.height / 3);
|
|
401
|
+
await page.mouse.click(mapDiv.width / 3, mapDiv.height / 3);
|
|
402
|
+
|
|
403
|
+
await changeMode({ page, mode: "polygon" });
|
|
404
|
+
await drawRectangularPolygon({ mapDiv, page });
|
|
405
|
+
|
|
406
|
+
await expectPaths({ page, count: 3 });
|
|
407
|
+
|
|
408
|
+
const button = page.getByText("clear");
|
|
409
|
+
await button.click();
|
|
410
|
+
|
|
411
|
+
await expectPaths({ page, count: 0 });
|
|
412
|
+
});
|
|
413
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Page, expect } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
export const pageUrl = "http://localhost:3000/";
|
|
4
|
+
|
|
5
|
+
export const setupMap = async ({
|
|
6
|
+
page,
|
|
7
|
+
}: {
|
|
8
|
+
page: Page;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}> => {
|
|
15
|
+
await page.goto(pageUrl);
|
|
16
|
+
|
|
17
|
+
const mapDiv = await page.getByRole("application");
|
|
18
|
+
|
|
19
|
+
// Get the bounding box of the div
|
|
20
|
+
const mapBoundingBox = await mapDiv.boundingBox();
|
|
21
|
+
|
|
22
|
+
if (!mapBoundingBox) {
|
|
23
|
+
throw new Error();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return mapBoundingBox;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const changeMode = async ({
|
|
30
|
+
page,
|
|
31
|
+
mode,
|
|
32
|
+
}: {
|
|
33
|
+
page: Page;
|
|
34
|
+
mode:
|
|
35
|
+
| "point"
|
|
36
|
+
| "polygon"
|
|
37
|
+
| "linestring"
|
|
38
|
+
| "select"
|
|
39
|
+
| "rectangle"
|
|
40
|
+
| "circle"
|
|
41
|
+
| "greatcircle";
|
|
42
|
+
}) => {
|
|
43
|
+
const modeText = mode.charAt(0).toUpperCase() + mode.slice(1);
|
|
44
|
+
const buttons = page.getByTestId("buttons");
|
|
45
|
+
const button = buttons.getByText(modeText, { exact: true });
|
|
46
|
+
|
|
47
|
+
// Click the mode button
|
|
48
|
+
await button.click();
|
|
49
|
+
|
|
50
|
+
// Ensure it has been clickde and updatedc correctly
|
|
51
|
+
const color = await button.evaluate((el) =>
|
|
52
|
+
window.getComputedStyle(el).getPropertyValue("color"),
|
|
53
|
+
);
|
|
54
|
+
expect(color).toBe("rgb(39, 204, 255)"); // We set hex but it gets computed to rgb
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const expectPaths = async ({
|
|
58
|
+
page,
|
|
59
|
+
count,
|
|
60
|
+
}: {
|
|
61
|
+
page: Page;
|
|
62
|
+
count: number;
|
|
63
|
+
}) => {
|
|
64
|
+
const selector = "svg > g > path";
|
|
65
|
+
|
|
66
|
+
if (count > 0) {
|
|
67
|
+
await page.waitForSelector(selector);
|
|
68
|
+
expect(await page.locator(selector).count()).toBe(count);
|
|
69
|
+
} else {
|
|
70
|
+
await expect(await page.locator(selector).count()).toBe(0);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const expectPathDimensions = async ({
|
|
75
|
+
page,
|
|
76
|
+
width,
|
|
77
|
+
height,
|
|
78
|
+
}: {
|
|
79
|
+
page: Page;
|
|
80
|
+
width: number;
|
|
81
|
+
height: number;
|
|
82
|
+
}) => {
|
|
83
|
+
const selector = "svg > g > path";
|
|
84
|
+
|
|
85
|
+
const boundingBox = await page.locator(selector).boundingBox();
|
|
86
|
+
|
|
87
|
+
expect(boundingBox?.width).toBe(width);
|
|
88
|
+
expect(boundingBox?.height).toBe(height);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const expectGroupPosition = async ({
|
|
92
|
+
page,
|
|
93
|
+
x,
|
|
94
|
+
y,
|
|
95
|
+
}: {
|
|
96
|
+
page: Page;
|
|
97
|
+
x: number;
|
|
98
|
+
y: number;
|
|
99
|
+
}) => {
|
|
100
|
+
const selector = "svg > g > path";
|
|
101
|
+
|
|
102
|
+
const boundingBox = await page.locator(selector).boundingBox();
|
|
103
|
+
|
|
104
|
+
expect(boundingBox?.x).toBe(x);
|
|
105
|
+
expect(boundingBox?.y).toBe(y);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const drawRectangularPolygon = async ({
|
|
109
|
+
mapDiv,
|
|
110
|
+
page,
|
|
111
|
+
}: {
|
|
112
|
+
mapDiv: {
|
|
113
|
+
x: number;
|
|
114
|
+
y: number;
|
|
115
|
+
width: number;
|
|
116
|
+
height: number;
|
|
117
|
+
};
|
|
118
|
+
page: Page;
|
|
119
|
+
}) => {
|
|
120
|
+
// Draw a rectangle
|
|
121
|
+
const sideLength = 100;
|
|
122
|
+
const halfLength = sideLength / 2;
|
|
123
|
+
const centerX = mapDiv.width / 2;
|
|
124
|
+
const centerY = mapDiv.height / 2;
|
|
125
|
+
const topLeft = { x: centerX - halfLength, y: centerY - halfLength };
|
|
126
|
+
const topRight = { x: centerX + halfLength, y: centerY - halfLength };
|
|
127
|
+
const bottomLeft = { x: centerX - halfLength, y: centerY + halfLength };
|
|
128
|
+
const bottomRight = { x: centerX + halfLength, y: centerY + halfLength };
|
|
129
|
+
await page.mouse.click(topLeft.x, topLeft.y);
|
|
130
|
+
await page.mouse.click(topRight.x, topRight.y);
|
|
131
|
+
await page.mouse.click(bottomRight.x, bottomRight.y);
|
|
132
|
+
await page.mouse.click(bottomLeft.x, bottomLeft.y);
|
|
133
|
+
await page.mouse.click(bottomLeft.x, bottomLeft.y); // Closed
|
|
134
|
+
|
|
135
|
+
return { topLeft, topRight, bottomRight, bottomLeft };
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const drawTwoClickShape = async ({
|
|
139
|
+
mapDiv,
|
|
140
|
+
page,
|
|
141
|
+
}: {
|
|
142
|
+
mapDiv: {
|
|
143
|
+
x: number;
|
|
144
|
+
y: number;
|
|
145
|
+
width: number;
|
|
146
|
+
height: number;
|
|
147
|
+
};
|
|
148
|
+
page: Page;
|
|
149
|
+
}) => {
|
|
150
|
+
// Draw a rectangle
|
|
151
|
+
const sideLength = 100;
|
|
152
|
+
const halfLength = sideLength / 2;
|
|
153
|
+
const centerX = mapDiv.width / 2;
|
|
154
|
+
const centerY = mapDiv.height / 2;
|
|
155
|
+
const topLeft = { x: centerX - halfLength, y: centerY - halfLength };
|
|
156
|
+
const topRight = { x: centerX + halfLength, y: centerY - halfLength };
|
|
157
|
+
const bottomLeft = { x: centerX - halfLength, y: centerY + halfLength };
|
|
158
|
+
const bottomRight = { x: centerX + halfLength, y: centerY + halfLength };
|
|
159
|
+
await page.mouse.click(topLeft.x, topLeft.y);
|
|
160
|
+
|
|
161
|
+
await page.mouse.click(bottomRight.x, bottomRight.y); // Closed
|
|
162
|
+
|
|
163
|
+
return { topLeft, topRight, bottomRight, bottomLeft };
|
|
164
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const Dotenv = require("dotenv-webpack");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
mode: "development",
|
|
6
|
+
entry: "./src/index.ts",
|
|
7
|
+
devtool: "inline-source-map",
|
|
8
|
+
plugins: [new Dotenv()],
|
|
9
|
+
module: {
|
|
10
|
+
rules: [
|
|
11
|
+
{
|
|
12
|
+
test: /\.tsx?$/,
|
|
13
|
+
use: "ts-loader",
|
|
14
|
+
exclude: /node_modules/,
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
resolve: {
|
|
19
|
+
extensions: [".tsx", ".ts", ".js"],
|
|
20
|
+
},
|
|
21
|
+
output: {
|
|
22
|
+
filename: "bundle.js",
|
|
23
|
+
path: path.resolve(__dirname, "dist"),
|
|
24
|
+
},
|
|
25
|
+
devServer: {
|
|
26
|
+
static: {
|
|
27
|
+
directory: path.join(__dirname, "public"),
|
|
28
|
+
},
|
|
29
|
+
liveReload: !process.env.CI,
|
|
30
|
+
watchFiles: !process.env.CI
|
|
31
|
+
? ["./src", "./*.{js,json,ts,html}", "../src", "../*.{js,json,ts,html}"]
|
|
32
|
+
: [],
|
|
33
|
+
compress: true,
|
|
34
|
+
port: 3000,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
console.log("===== Using @swc/jest ======");
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
transform: {
|
|
5
|
+
"^.+\\.(t|j)sx?$": "@swc/jest",
|
|
6
|
+
},
|
|
7
|
+
testPathIgnorePatterns: [
|
|
8
|
+
"<rootDir>/node_modules/",
|
|
9
|
+
"<rootDir>/e2e/",
|
|
10
|
+
"<rootDir>/docs/",
|
|
11
|
+
"<rootDir>/coverage/",
|
|
12
|
+
"<rootDir>/scripts/",
|
|
13
|
+
"<rootDir>/guides/",
|
|
14
|
+
],
|
|
15
|
+
coveragePathIgnorePatterns: ["<rootDir>/src/test/", "<rootDir>/e2e/"],
|
|
16
|
+
setupFilesAfterEnv: ["<rootDir>/src/test/jest.matchers.ts"],
|
|
17
|
+
};
|