Skip to content

Commit dca0b55

Browse files
authored
Merge pull request #311 from debrief/hotfix-initialise-graph-size
Introduce e2e testing
2 parents eb299ae + e0a0ffc commit dca0b55

File tree

9 files changed

+287
-11
lines changed

9 files changed

+287
-11
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Playwright Tests
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
name: Run Playwright Tests
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
# Checkout the code
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
19+
# Set up Node.js
20+
- name: Set up Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: 18
24+
cache: 'yarn'
25+
26+
# Install dependencies
27+
- name: Install dependencies
28+
run: yarn install
29+
30+
# Install Playwright browsers
31+
- name: Install Playwright browsers
32+
run: yarn playwright install --with-deps
33+
34+
# Build the application
35+
- name: Build application
36+
run: yarn build
37+
38+
# Run Playwright tests
39+
- name: Run Playwright tests
40+
run: yarn test:e2e
41+
42+
# Upload test results
43+
- name: Upload test results
44+
if: always()
45+
uses: actions/upload-artifact@v4
46+
with:
47+
name: playwright-report
48+
path: playwright-report/
49+
retention-days: 30

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,7 @@ build/
136136

137137
# Electron-Forge
138138
out/
139+
140+
# Playwright
141+
playwright-report/
142+
test-results/

jest.config.mjs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,28 @@
11
/** @type {import('ts-jest').JestConfigWithTsJest} **/
22
export default {
33
testEnvironment: "node",
4+
// Only run tests in the src directory
5+
testMatch: [
6+
"<rootDir>/src/**/*.test.[jt]s?(x)",
7+
"<rootDir>/src/**/__tests__/**/*.[jt]s?(x)"
8+
],
9+
// Explicitly ignore tests in the Playwright e2e directory
10+
testPathIgnorePatterns: [
11+
"<rootDir>/node_modules/",
12+
"<rootDir>/e2e/",
13+
"<rootDir>/tests/"
14+
],
415
moduleNameMapper: {
5-
'^leaflet$': '<rootDir>/src/mock/leaflet.js',
16+
'^leaflet$': '<rootDir>/src/mock/leaflet.js'
617
},
718
transform: {
8-
"^.+.tsx?$": ["ts-jest",{}],
19+
"^.+.tsx?$": ["ts-jest",{}]
920
},
1021
globals: {
1122
'ts-jest': {
1223
tsconfig: {
1324
esModuleInterop: true
14-
},
15-
},
16-
},
17-
};
25+
}
26+
}
27+
}
28+
}

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"prepare": "husky",
1414
"preview": "vite preview",
1515
"test": "jest",
16+
"test:e2e": "playwright test",
17+
"test:e2e:ui": "playwright test --ui",
1618
"storybook": "storybook dev -p 6006",
1719
"build-storybook": "npx storybook build -c .storybook -o ./build/storybook",
1820
"start-electron": "electron-forge start",
@@ -72,6 +74,7 @@
7274
"@electron/fuses": "^1.8.0",
7375
"@eslint/js": "^9.14.0",
7476
"@jest/globals": "^29.7.0",
77+
"@playwright/test": "^1.51.1",
7578
"@storybook/addon-essentials": "^8.4.7",
7679
"@storybook/addon-interactions": "8.4.7",
7780
"@storybook/addon-onboarding": "8.4.7",

playwright.config.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { defineConfig, devices } from '@playwright/test'
2+
3+
export default defineConfig({
4+
testDir: './tests',
5+
fullyParallel: true,
6+
forbidOnly: !!process.env.CI,
7+
retries: process.env.CI ? 2 : 0,
8+
workers: process.env.CI ? 1 : undefined,
9+
reporter: 'html',
10+
use: {
11+
baseURL: 'http://localhost:5173',
12+
trace: 'on-first-retry',
13+
screenshot: 'only-on-failure',
14+
},
15+
projects: [
16+
{
17+
name: 'chromium',
18+
use: { ...devices['Desktop Chrome'] },
19+
},
20+
],
21+
webServer: {
22+
command: 'yarn dev',
23+
url: 'http://localhost:5173',
24+
reuseExistingServer: !process.env.CI,
25+
timeout: 120 * 1000, // 2 minutes
26+
},
27+
})

src/components/ControlPanel/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ const ControlPanel: React.FC<TimeProps> = ({ bounds, handleSave, isDirty }) => {
143143
<Button
144144
color='primary'
145145
variant='outlined'
146+
className={`${tooltip.toLowerCase().replace(/\s+/g, '-')}`}
146147
icon={icon}
147148
disabled={!time.filterApplied}
148149
onClick={() => doStep(forward, large)}
@@ -205,6 +206,7 @@ const ControlPanel: React.FC<TimeProps> = ({ bounds, handleSave, isDirty }) => {
205206
<Button
206207
style={buttonStyle}
207208
disabled={bounds === null}
209+
className='apply-time-filter'
208210
color='primary'
209211
variant={time.filterApplied ? 'solid' : 'outlined'}
210212
onClick={() => setFilterApplied(value => !value)}
@@ -248,18 +250,19 @@ const ControlPanel: React.FC<TimeProps> = ({ bounds, handleSave, isDirty }) => {
248250
</thead>
249251
<tbody>
250252
<tr style={{ fontFamily: 'monospace' }}>
251-
<td>{timeStr(time.start)}</td>
253+
<td className='time-start-txt'>{timeStr(time.start)}</td>
252254
<td>
253255
<AutoComplete
254256
style={{ width: 100 }}
257+
className='time-step-input'
255258
value={stepTxt}
256259
onChange={(value) => setStepTxt(value)}
257260
defaultOpen={false}
258261
options={StepOptions}
259262
placeholder='00h30m'
260263
/>
261264
</td>
262-
<td>{timeStr(time.end)}</td>
265+
<td className='time-end-txt'>{timeStr(time.end)}</td>
263266
</tr>
264267
<tr style={{ fontFamily: 'monospace' }}>
265268
<td>

src/helpers/tests/rangeBearingCalc.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import { GraphDatum } from '../../types'
55
// Import the processBearingData function - we need to make it accessible for testing
66
// Since it's not exported directly, we'll test it through a test export
77
// This requires a small modification to the original file to expose it for testing
8-
import { BEARING_DATA, processBearingDataForTest, rangeBearingCalc } from '../calculations/rangeBearingCalc'
9-
import { toShortDTG } from '../toDTG'
8+
import { processBearingDataForTest, rangeBearingCalc } from '../calculations/rangeBearingCalc'
109

1110
describe('processBearingData', () => {
1211
it('should return the original data if less than 2 points', () => {
@@ -107,7 +106,8 @@ describe('processBearingData', () => {
107106
const trackVang = track1
108107
const polySap11 = zones[0]
109108
const calcData = rangeBearingCalc.calculate([trackVang, polySap11], trackVang.id as string)
110-
console.table(calcData.filter(d => d.extraProp === BEARING_DATA)[0].data.map(d => {return {date: toShortDTG(new Date(d.date)), value: d.value}}))
109+
// console.table(calcData.filter(d => d.extraProp === BEARING_DATA)[0].data.map(d => {return {date: toShortDTG(new Date(d.date)), value: d.value}}))
110+
expect(calcData[0].data.length).toEqual(1081)
111111
})
112112
})
113113

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { test, expect } from '@playwright/test'
2+
const plotName = 'Test plot'
3+
4+
test('Open Sample Plot, apply time filter, and step forward in time', async ({ page }) => {
5+
// Navigate to the application
6+
await page.goto('/')
7+
8+
// Wait for the application to load
9+
await page.waitForSelector('button:has-text("Sample plot")')
10+
11+
// Click on Sample Plot button to create a new sample plot
12+
await page.click('button:has-text("Sample plot")')
13+
14+
// Wait for the document name modal to appear
15+
await page.waitForSelector('div.ant-modal-content')
16+
17+
// Enter a name for the document in the input field
18+
await page.locator('input.ant-input').fill(plotName)
19+
20+
// Click the OK button to confirm
21+
await page.locator('button:has-text("OK")').click()
22+
23+
// Wait for the plot to load after modal confirmation
24+
await page.waitForSelector('.flexlayout__tab_button_content')
25+
await page.waitForSelector('.step-forward')
26+
27+
// Verify that the plot has been created
28+
expect (await page.locator('.flexlayout__tab_button_content').first().textContent()).toEqual(plotName)
29+
30+
// Find the step forward button (TimeButton component with StepForwardOutlined icon)
31+
// Based on the application code, it's a button with tooltip 'Step forward'
32+
const stepForwardButton = page.locator('.step-forward')
33+
34+
// Check if the TimePeriod component is visible
35+
await expect(page.locator('.time-period-panel')).toBeVisible()
36+
37+
// check the time start and end are outer period of data
38+
const timeStart = page.locator('.time-start-txt')
39+
const timeEnd = page.locator('.time-end-txt')
40+
expect(await timeStart.textContent()).toEqual('Nov 141616Z')
41+
expect(await timeEnd.textContent()).toEqual('Nov 151017Z')
42+
43+
// Get the initial time text before any actions
44+
const timeTextBeforeFilter = await page.locator('.time-period-panel p').textContent()
45+
console.log('Time period before filter:', timeTextBeforeFilter)
46+
47+
// check time step buttons are disabled
48+
await expect(page.locator('.step-forward')).toBeDisabled()
49+
await expect(page.locator('.step-backward')).toBeDisabled()
50+
51+
// Get the time text after clicking step forward (should be unchanged)
52+
const timeTextAfterClick = await page.locator('.time-period-panel p').textContent()
53+
console.log('Time period after clicking step forward (before filter):', timeTextAfterClick)
54+
55+
// Verify that the time text has NOT changed (because filter is not applied)
56+
expect(timeTextAfterClick).toEqual(timeTextBeforeFilter)
57+
console.log('Verified: Time period did not change when filter is not applied')
58+
59+
// Find and click the time filter button in the control panel
60+
// Based on the application code, it's a button with FilterOutlined/FilterFilled icon
61+
const timeFilterButton = page.locator('.apply-time-filter')
62+
63+
// Check the time filter is currently disabled
64+
await expect(page.locator('.time-step-input')).toHaveClass(/.*ant-select-disabled.*/)
65+
66+
// Alternative selector if the above doesn't work
67+
// const timeFilterButton = page.locator('button', { has: page.locator('.anticon-filter, .anticon-filter-filled') })
68+
await timeFilterButton.click()
69+
70+
// check the time start and end are clipped to new values
71+
expect(await timeStart.textContent()).toEqual('Nov 141600Z')
72+
expect(await timeEnd.textContent()).toEqual('Nov 141700Z')
73+
74+
// Verify that the form containing the time-step-input is no longer disabled
75+
// This is a more accurate way to confirm the time filter is active
76+
await expect(page.locator('.time-step-input')).not.toHaveClass(/.*ant-select-disabled.*/)
77+
78+
// Now click the step forward button after applying the filter (should work now)
79+
await stepForwardButton.click()
80+
81+
// Verify that the time has changed after stepping forward
82+
83+
// Wait for any animations or updates to complete
84+
await page.waitForTimeout(500)
85+
86+
// Now specifically check that the TimePeriod component is visible
87+
await expect(page.locator('.time-period-panel')).toBeVisible()
88+
89+
// Get the text content of the TimePeriod before stepping forward
90+
const initialTimeText = await page.locator('.time-period-panel p').textContent()
91+
console.log('Initial time period:', initialTimeText)
92+
93+
// Click the step forward button again
94+
await stepForwardButton.click()
95+
96+
// Wait for the time to update
97+
await page.waitForTimeout(500)
98+
99+
// Get the updated text content
100+
const updatedTimeText = await page.locator('.time-period-panel p').textContent()
101+
console.log('Updated time period:', updatedTimeText)
102+
103+
// Verify that the time text has changed
104+
expect(updatedTimeText).not.toEqual(initialTimeText)
105+
console.log('Time period changed successfully from', initialTimeText, 'to', updatedTimeText)
106+
107+
// check the time start and end times have moved forwards
108+
expect(await timeStart.textContent()).toEqual('Nov 141800Z')
109+
expect(await timeEnd.textContent()).toEqual('Nov 141900Z')
110+
111+
// get the large step fowards button
112+
const largeStepForwardButton = page.locator('.jump-to-end')
113+
await largeStepForwardButton.click()
114+
115+
// Wait for the time to update
116+
await page.waitForTimeout(500)
117+
118+
// Get the updated text content
119+
const updatedTimeText2 = await page.locator('.time-period-panel p').textContent()
120+
console.log('Updated time period:', updatedTimeText2)
121+
122+
// Verify that the time text has changed
123+
expect(updatedTimeText2).not.toEqual(initialTimeText)
124+
console.log('Time period changed successfully from', initialTimeText, 'to', updatedTimeText2)
125+
126+
// check the time start and end times have moved to end
127+
expect(await timeStart.textContent()).toEqual('Nov 151000Z')
128+
expect(await timeEnd.textContent()).toEqual('Nov 151100Z')
129+
130+
// get the large step backwards button
131+
const largeStepBackButton = page.locator('.jump-to-start')
132+
await largeStepBackButton.click()
133+
134+
// Wait for the time to update
135+
await page.waitForTimeout(500)
136+
137+
// check the time start and end times have moved to start
138+
expect(await timeStart.textContent()).toEqual('Nov 141600Z')
139+
expect(await timeEnd.textContent()).toEqual('Nov 141700Z')
140+
141+
// disable time filter
142+
await timeFilterButton.click()
143+
144+
// Wait for the time to update
145+
await page.waitForTimeout(500)
146+
147+
// check time step interval is disabled
148+
await expect(page.locator('.time-step-input')).toHaveClass(/.*ant-select-disabled.*/)
149+
150+
// check time step buttons are disabled
151+
await expect(page.locator('.step-forward')).toBeDisabled()
152+
await expect(page.locator('.step-backward')).toBeDisabled()
153+
})

yarn.lock

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,6 +2071,13 @@
20712071
mkdirp "^1.0.4"
20722072
rimraf "^3.0.2"
20732073

2074+
"@playwright/test@^1.51.1":
2075+
version "1.51.1"
2076+
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.51.1.tgz#75357d513221a7be0baad75f01e966baf9c41a2e"
2077+
integrity sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==
2078+
dependencies:
2079+
playwright "1.51.1"
2080+
20742081
"@rc-component/async-validator@^5.0.3":
20752082
version "5.0.4"
20762083
resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz#5291ad92f00a14b6766fc81735c234277f83e948"
@@ -6982,6 +6989,11 @@ fs.realpath@^1.0.0:
69826989
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
69836990
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
69846991

6992+
fsevents@2.3.2:
6993+
version "2.3.2"
6994+
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
6995+
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
6996+
69856997
fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3:
69866998
version "2.3.3"
69876999
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
@@ -9390,6 +9402,20 @@ pkg-dir@^4.2.0:
93909402
dependencies:
93919403
find-up "^4.0.0"
93929404

9405+
playwright-core@1.51.1:
9406+
version "1.51.1"
9407+
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.51.1.tgz#d57f0393e02416f32a47cf82b27533656a8acce1"
9408+
integrity sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==
9409+
9410+
playwright@1.51.1:
9411+
version "1.51.1"
9412+
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.51.1.tgz#ae1467ee318083968ad28d6990db59f47a55390f"
9413+
integrity sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==
9414+
dependencies:
9415+
playwright-core "1.51.1"
9416+
optionalDependencies:
9417+
fsevents "2.3.2"
9418+
93939419
plist@^3.0.0, plist@^3.0.5, plist@^3.1.0:
93949420
version "3.1.0"
93959421
resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9"

0 commit comments

Comments
 (0)