Skip to content

Commit 3796bd7

Browse files
Merge branch 'test' into kim/playwright-geo-tests
2 parents fe14155 + a67b7fd commit 3796bd7

16 files changed

Lines changed: 458 additions & 68 deletions

.github/workflows/e2etest.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ jobs:
1313
test:
1414
timeout-minutes: 60
1515
runs-on: ubuntu-latest
16+
container:
17+
image: mcr.microsoft.com/playwright:v1.59.1-noble
18+
options: --user 1001
1619
environment:
1720
name: playwright
1821
deployment: false
@@ -34,8 +37,6 @@ jobs:
3437
run: mkdir -p playwright/.auth
3538
- name: Import Credentials
3639
run: echo "${{ secrets.TEST_ACCOUNT_CREDS }}" | base64 --decode > playwright/.auth/creds.json
37-
- name: Install Playwright Browsers
38-
run: npx playwright install --with-deps
3940
- name: Run Playwright tests
4041
run: npm run e2e -- --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
4142
- uses: actions/upload-artifact@v4
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test('Import a geojson point file', async ({ page }) => {
4+
await page.goto('/');
5+
await page.locator('app-aoi-filter').getByText('arrow_drop_down').click();
6+
7+
await page
8+
.locator('app-aoi-filter')
9+
.locator('input[type="file"]')
10+
.setInputFiles('./e2e/geographic/assets/point.geojson');
11+
12+
await page.waitForResponse((r) => r.url().includes('files_to_wkt'));
13+
14+
const value = await page
15+
.locator('app-aoi-filter')
16+
.locator('input[name="searchPolygon"]')
17+
.inputValue();
18+
expect(value).toContain('POINT(-102.4805 38.7541)');
19+
});
20+
21+
test('Import a KML file', async ({ page }) => {
22+
await page.goto('/');
23+
await page.locator('app-aoi-filter').getByText('arrow_drop_down').click();
24+
25+
await page
26+
.locator('app-aoi-filter')
27+
.locator('input[type="file"]')
28+
.setInputFiles('./e2e/geographic/assets/basic.kml');
29+
30+
await page.waitForResponse((r) => r.url().includes('files_to_wkt'));
31+
32+
const value = await page
33+
.locator('app-aoi-filter')
34+
.locator('input[name="searchPolygon"]')
35+
.inputValue();
36+
expect(value).toContain('POLYGON');
37+
expect(value).toContain('-43.7081');
38+
});
39+
40+
test('Import a .shp shape file', async ({ page }) => {
41+
await page.goto('/');
42+
await page.locator('app-aoi-filter').getByText('arrow_drop_down').click();
43+
44+
await page
45+
.locator('app-aoi-filter')
46+
.locator('input[type="file"]')
47+
.setInputFiles('./e2e/geographic/assets/boundary.shp');
48+
49+
await page.waitForResponse((r) => r.url().includes('files_to_wkt'));
50+
51+
const value = await page
52+
.locator('app-aoi-filter')
53+
.locator('input[name="searchPolygon"]')
54+
.inputValue();
55+
expect(value).toContain('POLYGON');
56+
});
57+
58+
test('Import a .zip shape file', async ({ page }) => {
59+
await page.goto('/');
60+
await page.locator('app-aoi-filter').getByText('arrow_drop_down').click();
61+
62+
await page
63+
.locator('app-aoi-filter')
64+
.locator('input[type="file"]')
65+
.setInputFiles('./e2e/geographic/assets/boundary.zip');
66+
67+
await page.waitForResponse((r) => r.url().includes('files_to_wkt'));
68+
69+
const value = await page
70+
.locator('app-aoi-filter')
71+
.locator('input[name="searchPolygon"]')
72+
.inputValue();
73+
expect(value).toContain('POLYGON');
74+
});
75+
76+
test('Import multiple geojson files sequentially', async ({ page }) => {
77+
await page.goto('/');
78+
await page.locator('app-aoi-filter').getByText('arrow_drop_down').click();
79+
80+
await page
81+
.locator('app-aoi-filter')
82+
.locator('input[type="file"]')
83+
.setInputFiles('./e2e/geographic/assets/basic.geojson');
84+
85+
await page.waitForResponse((r) => r.url().includes('files_to_wkt'));
86+
87+
const firstValue = await page
88+
.locator('app-aoi-filter')
89+
.locator('input[name="searchPolygon"]')
90+
.inputValue();
91+
expect(firstValue).toContain('POLYGON');
92+
93+
await page
94+
.locator('app-aoi-filter')
95+
.locator('input[type="file"]')
96+
.setInputFiles('./e2e/geographic/assets/north-carolina.geojson');
97+
98+
await page.waitForResponse((r) => r.url().includes('files_to_wkt'));
99+
100+
const secondValue = await page
101+
.locator('app-aoi-filter')
102+
.locator('input[name="searchPolygon"]')
103+
.inputValue();
104+
expect(secondValue).toContain('POLYGON');
105+
expect(secondValue).not.toEqual(firstValue);
106+
});
107+
108+
test('Import rejects invalid file type', async ({ page }) => {
109+
await page.goto('/');
110+
await page.locator('app-aoi-filter').getByText('arrow_drop_down').click();
111+
112+
await page
113+
.locator('app-aoi-filter')
114+
.locator('input[type="file"]')
115+
.setInputFiles('./e2e/geographic/assets/invalid.csv');
116+
117+
const aoiValue = await page
118+
.locator('app-aoi-filter')
119+
.locator('input[name="searchPolygon"]')
120+
.inputValue();
121+
expect(aoiValue).toBe('');
122+
});

e2e/geographic/assets/basic.kml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<kml xmlns="http://www.opengis.net/kml/2.2">
3+
<Document>
4+
<Placemark>
5+
<Polygon>
6+
<outerBoundaryIs>
7+
<LinearRing>
8+
<coordinates>
9+
-43.7081,-16.7393,0
10+
-43.7068,-16.9726,0
11+
-43.4056,-16.9702,0
12+
-43.412,-16.7345,0
13+
-43.7081,-16.7393,0
14+
</coordinates>
15+
</LinearRing>
16+
</outerBoundaryIs>
17+
</Polygon>
18+
</Placemark>
19+
</Document>
20+
</kml>

e2e/geographic/assets/boundary.shp

236 Bytes
Binary file not shown.

e2e/geographic/assets/boundary.zip

275 Bytes
Binary file not shown.

e2e/geographic/assets/invalid.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
name,lat,lon
2+
test,64.2,-149.5
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"type": "Feature",
6+
"geometry": {
7+
"type": "Polygon",
8+
"coordinates": [
9+
[
10+
[-80.9379, 35.2271],
11+
[-80.8431, 35.2271],
12+
[-80.8431, 35.2826],
13+
[-80.9379, 35.2826],
14+
[-80.9379, 35.2271]
15+
]
16+
]
17+
},
18+
"properties": {}
19+
}
20+
]
21+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type": "Feature",
3+
"geometry": {
4+
"type": "Point",
5+
"coordinates": [-102.4805, 38.7541]
6+
},
7+
"properties": {}
8+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { test, expect } from '@playwright/test';
2+
import { waitForASFAPIResponse } from 'e2e/helpers';
3+
4+
test('ERS subtype E2 filter returns matching results', async ({ page }) => {
5+
await page.goto('/');
6+
await page.getByRole('button', { name: 'Sentinel-' }).click();
7+
await page
8+
.getByRole('menuitem', { name: 'ERS Primarily SAR imagery' })
9+
.click();
10+
11+
await page.getByRole('button', { name: 'Filters', exact: true }).click();
12+
await expect(page.getByText('subtypes selected')).toContainText(
13+
'0/2 subtypes selected',
14+
);
15+
16+
await page.getByText('Subtype', { exact: true }).click();
17+
await page.getByRole('option', { name: 'E2' }).click();
18+
await page.keyboard.press('Escape');
19+
20+
const responsePromise = waitForASFAPIResponse(page);
21+
await page
22+
.locator('#mat-button-toggle-6-button')
23+
.getByRole('button', { name: 'SEARCH' })
24+
.click();
25+
await responsePromise;
26+
27+
await expect(page.locator('app-info-bar')).toContainText('Dataset: E2');
28+
await expect(page.locator('mat-card-header')).toContainText('E2');
29+
});

e2e/geographic/geocode.spec.ts

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { test, expect } from '@playwright/test';
2+
import { waitForASFAPIResponse } from 'e2e/helpers';
3+
4+
const MAPBOX_API_GLOB = 'https://api.mapbox.com/geocoding/**';
5+
6+
const MOCK_RESPONSES = {
7+
Tibet: {
8+
type: 'FeatureCollection',
9+
features: [
10+
{
11+
id: 'region.tibet',
12+
type: 'Feature',
13+
place_name: 'Tibet Autonomous Region, China',
14+
geometry: { type: 'Point', coordinates: [88.0439, 31.5534] },
15+
bbox: [78.3955, 26.8562, 99.1159, 36.4833],
16+
},
17+
],
18+
},
19+
'Big Bear Lake': {
20+
type: 'FeatureCollection',
21+
features: [
22+
{
23+
id: 'place.bigbear',
24+
type: 'Feature',
25+
place_name: 'Big Bear Lake, California, United States',
26+
geometry: { type: 'Point', coordinates: [-116.9115, 34.2437] },
27+
bbox: [-116.95, 34.2, -116.87, 34.28],
28+
},
29+
],
30+
},
31+
'Sierra Le': {
32+
type: 'FeatureCollection',
33+
features: [
34+
{
35+
id: 'place.sierra1',
36+
type: 'Feature',
37+
place_name: 'Sierra Leone',
38+
geometry: { type: 'Point', coordinates: [-11.7799, 8.4606] },
39+
bbox: [-13.3, 6.9, -10.3, 10.0],
40+
},
41+
{
42+
id: 'place.sierra2',
43+
type: 'Feature',
44+
place_name:
45+
'Sierra Leone Avenue, Nassau, New Providence, Bahamas',
46+
geometry: { type: 'Point', coordinates: [-77.3788, 25.0113] },
47+
bbox: [-77.38, 25.01, -77.37, 25.02],
48+
},
49+
],
50+
},
51+
};
52+
53+
async function mockGeocoding(page: any) {
54+
await page.route(MAPBOX_API_GLOB, (route: any) => {
55+
const url = route.request().url();
56+
let response = MOCK_RESPONSES['Tibet'];
57+
58+
for (const key of Object.keys(MOCK_RESPONSES)) {
59+
if (url.includes(encodeURIComponent(key))) {
60+
response = MOCK_RESPONSES[key];
61+
break;
62+
}
63+
}
64+
65+
route.fulfill({
66+
status: 200,
67+
contentType: 'application/json',
68+
body: JSON.stringify(response),
69+
});
70+
});
71+
}
72+
73+
test('Place name is geocoded to WKT AOI and returns search results', async ({
74+
page,
75+
}) => {
76+
await mockGeocoding(page);
77+
await page.goto('/');
78+
await page.getByRole('button', { name: 'Sentinel-' }).click();
79+
await page.getByRole('menuitem', { name: 'S1 Burst' }).click();
80+
81+
const aoiFilter = page.locator('app-aoi-filter');
82+
await aoiFilter.locator('.additional-aoi-toggle').click();
83+
84+
const geocodeInput = aoiFilter
85+
.locator('app-geocode-selector')
86+
.getByLabel('Search for a location');
87+
await geocodeInput.fill('Tibet');
88+
await page.getByRole('option').first().click();
89+
90+
await expect(aoiFilter.locator('input[name="searchPolygon"]')).toHaveValue(
91+
/POINT\(88\.0439 31\.5534\)/,
92+
);
93+
await expect(geocodeInput).toHaveValue(/Tibet Autonomous Region.*China/);
94+
95+
const responsePromise = waitForASFAPIResponse(page);
96+
await page
97+
.locator('#mat-button-toggle-8-button')
98+
.getByRole('button', { name: 'SEARCH' })
99+
.click();
100+
await responsePromise;
101+
102+
await expect(page.locator('mat-card-header').first()).toBeVisible();
103+
});
104+
105+
test('Place name geocode pans the map to the entered location', async ({
106+
page,
107+
}) => {
108+
await mockGeocoding(page);
109+
await page.goto('/');
110+
111+
const aoiFilter = page.locator('app-aoi-filter');
112+
await aoiFilter.locator('.additional-aoi-toggle').click();
113+
114+
const geocodeInput = aoiFilter
115+
.locator('app-geocode-selector')
116+
.getByLabel('Search for a location');
117+
await geocodeInput.fill('Big Bear Lake');
118+
await page.getByRole('option').first().click();
119+
120+
await expect(aoiFilter.locator('input[name="searchPolygon"]')).toHaveValue(
121+
/POINT\(-116\.9115 34\.2437\)/,
122+
);
123+
await expect(geocodeInput).toHaveValue(
124+
/Big Bear Lake.*California.*United States/,
125+
);
126+
127+
await expect(page).toHaveURL(/polygon=POINT/);
128+
});
129+
130+
test('Geocoded place name is cleared when AOI is manually updated', async ({
131+
page,
132+
}) => {
133+
await mockGeocoding(page);
134+
await page.goto('/');
135+
await page.getByRole('button', { name: 'Sentinel-' }).click();
136+
await page.getByRole('menuitem', { name: 'S1 Burst' }).click();
137+
138+
const aoiFilter = page.locator('app-aoi-filter');
139+
await aoiFilter.locator('.additional-aoi-toggle').click();
140+
141+
const geocodeInput = aoiFilter
142+
.locator('app-geocode-selector')
143+
.getByLabel('Search for a location');
144+
await geocodeInput.fill('Sierra Le');
145+
await page.getByRole('option').nth(1).click();
146+
147+
await expect(aoiFilter.locator('input[name="searchPolygon"]')).toHaveValue(
148+
/POINT\(-77\.3788 25\.0113\)/,
149+
);
150+
await expect(geocodeInput).toHaveValue(
151+
/Sierra Leone Avenue.*Nassau.*Bahamas/,
152+
);
153+
154+
await aoiFilter
155+
.locator('textarea[name="searchPolygonLarge"]')
156+
.fill('POINT(-120.6999 38.3044)');
157+
158+
const responsePromise = waitForASFAPIResponse(page);
159+
await page
160+
.locator('#mat-button-toggle-8-button')
161+
.getByRole('button', { name: 'SEARCH' })
162+
.click();
163+
await responsePromise;
164+
165+
await aoiFilter.locator('.additional-aoi-toggle').click();
166+
await expect(geocodeInput).toHaveValue('');
167+
});

0 commit comments

Comments
 (0)