Skip to content

Commit 95bdf04

Browse files
authored
test: Add Tests for Operator Page (#1112)
1 parent 995fd09 commit 95bdf04

File tree

6 files changed

+188
-47
lines changed

6 files changed

+188
-47
lines changed

tests/clearButton.spec.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import type { Locator, Page } from '@playwright/test'
22
import i18next from 'i18next'
3-
import Backend from 'i18next-fs-backend'
4-
import { test, expect, urlMatcher, setBrowserTime, getPastDate } from './utils'
5-
3+
import { test, expect, urlMatcher, setBrowserTime, getPastDate, loadTranslate } from './utils'
64
import Selectors from './SelectorsModel'
75

86
async function visitPage(page: Page, pageName: string, url: RegExp) {
@@ -25,13 +23,7 @@ async function selectLineNumberAndRoute(page: Page, lineNumber: Locator, route:
2523
test.describe('clearButton functionality', () => {
2624
test.beforeEach(async ({ page, advancedRouteFromHAR }) => {
2725
await page.route(/google-analytics\.com|googletagmanager\.com/, (route) => route.abort())
28-
await i18next.use(Backend).init({
29-
lng: 'he',
30-
backend: {
31-
loadPath: 'src/locale/{{lng}}.json',
32-
},
33-
})
34-
26+
await loadTranslate(i18next)
3527
advancedRouteFromHAR('tests/HAR/clearbutton.har', {
3628
updateContent: 'embed',
3729
update: false,

tests/dashboard.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from '@playwright/test'
2-
import { test, urlMatcher } from './utils'
2+
import { getPastDate, test, urlMatcher, waitForSkeletonsToHide } from './utils'
33

44
test.describe('dashboard tests', () => {
55
test.beforeEach(async ({ page, advancedRouteFromHAR }) => {
@@ -11,10 +11,10 @@ test.describe('dashboard tests', () => {
1111
url: /stride-api/,
1212
matcher: urlMatcher,
1313
})
14+
await page.clock.setFixedTime(getPastDate())
1415
await page.goto('/dashboard')
1516
await page.getByText('הקווים הגרועים ביותר').waitFor()
16-
const skeletons = await page.locator('.ant-skeleton').all()
17-
await Promise.all(skeletons.map((skeleton) => skeleton.waitFor({ state: 'hidden' })))
17+
await waitForSkeletonsToHide(page)
1818
})
1919

2020
test('page is working', async () => {})

tests/operator.spec.ts

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { expect, test, type Page } from '@playwright/test'
2+
import i18next from 'i18next'
3+
import { getPastDate, loadTranslate, waitForSkeletonsToHide } from './utils'
4+
import { operatorList } from 'src/pages/operator/data'
5+
6+
const getLabelValue = async (label: string, page: Page) => {
7+
const row = page.locator('table tr', { hasText: label })
8+
return await row.locator('td').nth(1).textContent()
9+
}
10+
11+
const selectRandomOperator = async (label: string, page: Page) => {
12+
await page.getByRole('combobox', { name: label }).click()
13+
const options = page.getByRole('option')
14+
const optionsCount = await options.count()
15+
const randomIndex = Math.floor(Math.random() * optionsCount)
16+
await options.nth(randomIndex).scrollIntoViewIfNeeded()
17+
await options.nth(randomIndex).click()
18+
return options.nth(randomIndex)
19+
}
20+
21+
test.describe('Operator Page Tests', () => {
22+
test.beforeEach(async ({ page }) => {
23+
await page.route(/google-analytics\.com|googletagmanager\.com/, (route) => route.abort())
24+
await loadTranslate(i18next)
25+
await page.clock.setFixedTime(getPastDate())
26+
await page.goto('/')
27+
await page
28+
.getByText(i18next.t('operator_title'), { exact: true })
29+
.and(page.getByRole('link'))
30+
.click()
31+
await page.getByRole('progressbar').waitFor({ state: 'hidden' })
32+
await test.step('Validate that the test is on the correct page', async () => {
33+
await expect(page).toHaveURL(/operator/)
34+
await expect(page.locator('h4')).toHaveText(i18next.t('operator_title'))
35+
})
36+
})
37+
38+
test('Test operator inputs', async ({ page }) => {
39+
await test.step('Validate inputs are disabled when no operator is selected', async () => {
40+
await expect(page.getByRole('combobox', { name: i18next.t('choose_operator') })).toBeEmpty()
41+
await expect(page.getByRole('textbox', { name: i18next.t('choose_date') })).toBeDisabled()
42+
await expect(
43+
page.getByRole('button', { name: i18next.t('operator.time_range.day') }),
44+
).toBeDisabled()
45+
await expect(
46+
page.getByRole('button', { name: i18next.t('operator.time_range.week') }),
47+
).toBeDisabled()
48+
await expect(
49+
page.getByRole('button', { name: i18next.t('operator.time_range.month') }),
50+
).toBeDisabled()
51+
})
52+
53+
await test.step('Select a random operator', async () => {
54+
await selectRandomOperator(i18next.t('choose_operator'), page)
55+
})
56+
57+
await test.step('Validate inputs are enabled after selecting an operator', async () => {
58+
await expect(page.getByRole('textbox', { name: i18next.t('choose_date') })).toBeEnabled()
59+
await expect(
60+
page.getByRole('button', { name: i18next.t('operator.time_range.day') }),
61+
).toBeEnabled()
62+
await expect(
63+
page.getByRole('button', { name: i18next.t('operator.time_range.day') }),
64+
).toHaveAttribute('aria-pressed', 'true')
65+
await expect(
66+
page.getByRole('button', { name: i18next.t('operator.time_range.week') }),
67+
).toBeEnabled()
68+
await expect(
69+
page.getByRole('button', { name: i18next.t('operator.time_range.month') }),
70+
).toBeEnabled()
71+
})
72+
})
73+
74+
test('Test operator info card', async ({ page }) => {
75+
await test.step('Select a random operator', async () => {
76+
await selectRandomOperator(i18next.t('choose_operator'), page)
77+
})
78+
await test.step('Validate operator info card details', async () => {
79+
const id = await getLabelValue(i18next.t('operator.ref'), page)
80+
if (!id) {
81+
throw new Error('Operator ID not found in operator info card')
82+
}
83+
const operator = operatorList.find((op) => op.ref === id)
84+
if (!operator) {
85+
throw new Error(`Operator with ID ${id} not found in operator list`)
86+
}
87+
const founded = await getLabelValue(i18next.t('operator.founded'), page)
88+
if (founded !== '-') {
89+
expect(Number(founded)).toEqual(operator.founded)
90+
}
91+
const area = await getLabelValue(i18next.t('operator.area_title'), page)
92+
if (area !== '-') {
93+
expect(area).toEqual(i18next.t(`operator.area.${operator.area}`))
94+
}
95+
const type = await getLabelValue(i18next.t('operator.type_title'), page)
96+
if (type !== '-') {
97+
expect(type).toEqual(i18next.t(`operator.type.${operator.type}`))
98+
}
99+
const website = await getLabelValue(i18next.t('operator.website'), page)
100+
if (website !== '-') {
101+
expect(website).toEqual(operator.website)
102+
}
103+
})
104+
})
105+
106+
test('Test operator gaps card', async ({ page }) => {
107+
await test.step('Select a random operator', async () => {
108+
await selectRandomOperator(i18next.t('choose_operator'), page)
109+
})
110+
111+
await test.step('Validate operator gaps', async () => {
112+
await waitForSkeletonsToHide(page)
113+
const planned = await getLabelValue(i18next.t('rides_planned'), page)
114+
const actual = await getLabelValue(i18next.t('rides_actual'), page)
115+
const missing = await getLabelValue(i18next.t('rides_missing'), page)
116+
if (planned !== '-' && actual !== '-' && missing !== '-') {
117+
expect(Number(missing)).toEqual(Number(planned) - Number(actual))
118+
} else {
119+
throw new Error('Operator gaps not loaded')
120+
}
121+
})
122+
})
123+
124+
test('Test operator routes card', async ({ page }) => {
125+
await test.step('Select a random operator', async () => {
126+
await selectRandomOperator(i18next.t('choose_operator'), page)
127+
})
128+
129+
await test.step('Validate operator routes', async () => {
130+
await waitForSkeletonsToHide(page)
131+
const table = page.locator('table').nth(2)
132+
const rows = table.locator('tbody tr')
133+
const totalText = await page.getByText(i18next.t('operator.total')).textContent()
134+
const total = Number(totalText?.split(' ')[i18next.language === 'en' ? 2 : 3] || 0)
135+
if (total !== 0) {
136+
const rowsCount = await rows.count()
137+
expect(rowsCount).toEqual(total)
138+
} else {
139+
throw new Error('Operator routes not loaded')
140+
}
141+
})
142+
})
143+
})

tests/timeline.spec.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
11
import i18next from 'i18next'
2-
import Backend from 'i18next-fs-backend'
32
import moment from 'moment'
43
import TimelinePage from '../src/test_pages/TimelinePage'
5-
import { getPastDate, test, expect, urlMatcher } from './utils'
4+
import { getPastDate, test, expect, urlMatcher, loadTranslate } from './utils'
65

76
test.describe('Timeline Page Tests', () => {
87
let timelinePage: TimelinePage
98

109
test.beforeEach(async ({ page, advancedRouteFromHAR }) => {
1110
await page.route(/google-analytics\.com|googletagmanager\.com/, (route) => route.abort())
12-
await i18next.use(Backend).init({
13-
lng: 'he',
14-
backend: {
15-
loadPath: 'src/locale/{{lng}}.json',
16-
},
17-
})
18-
11+
await loadTranslate(i18next)
1912
await advancedRouteFromHAR('tests/HAR/timeline.har', {
2013
updateContent: 'embed',
2114
update: false,

tests/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import * as crypto from 'crypto'
55
import { exec } from 'child_process'
66
import { Matcher, test as baseTest, customMatcher } from 'playwright-advanced-har'
77
import { BrowserContext, Page } from '@playwright/test'
8+
import { i18n } from 'i18next'
9+
import Backend from 'i18next-fs-backend'
810

911
const istanbulCLIOutput = path.join(process.cwd(), '.nyc_output')
1012

@@ -84,4 +86,20 @@ export const getBranch = () =>
8486
})
8587
})
8688

89+
export const waitForSkeletonsToHide = async (page: Page) => {
90+
while ((await page.locator('.ant-skeleton-content').count()) > 0) {
91+
await page.locator('.ant-skeleton-content').last().waitFor({ state: 'hidden' })
92+
}
93+
}
94+
95+
export const loadTranslate = async (i18next: i18n) => {
96+
await i18next.use(Backend).init({
97+
lng: 'he',
98+
fallbackLng: 'en',
99+
backend: {
100+
loadPath: 'src/locale/{{lng}}.json',
101+
},
102+
})
103+
}
104+
87105
export const expect = test.expect

tests/visual.spec.ts

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Eyes, Target, VisualGridRunner } from '@applitools/eyes-playwright'
22
import username from 'git-username'
3-
import { getBranch, test } from './utils'
3+
import { getBranch, getPastDate, test, waitForSkeletonsToHide } from './utils'
44

55
test.describe('Visual Tests', () => {
66
const eyes = new Eyes(new VisualGridRunner(), {
@@ -13,13 +13,15 @@ test.describe('Visual Tests', () => {
1313
},
1414
],
1515
})
16+
1617
test.beforeAll(async () => {
1718
setBatchName(eyes)
1819
await setEyesSettings(eyes)
1920
})
2021

2122
test.beforeEach(async ({ page }, testinfo) => {
2223
await page.route(/google-analytics\.com|googletagmanager\.com/, (route) => route.abort())
24+
await page.clock.setFixedTime(getPastDate())
2325
if (!process.env.APPLITOOLS_API_KEY) {
2426
eyes.setIsDisabled(true)
2527
console.log('APPLITOOLS_API_KEY is not defined, please ask noamgaash for the key')
@@ -44,23 +46,16 @@ test.describe('Visual Tests', () => {
4446
test('dashboard page should look good', async ({ page }) => {
4547
await page.goto('/dashboard')
4648
await page.getByText('אגד').first().waitFor()
47-
while ((await page.locator('.ant-skeleton-content').count()) > 0)
48-
await page.locator('.ant-skeleton-content').last().waitFor({ state: 'hidden' })
49+
await waitForSkeletonsToHide(page)
4950
await eyes.check(
5051
'dashboard page',
51-
Target.window()
52-
.layoutRegions('.chart', page.getByPlaceholder('DD/MM/YYYY'))
53-
.fully()
54-
.scrollRootElement('main'),
52+
Target.window().layoutRegions('.chart').fully().scrollRootElement('main'),
5553
)
5654
// scroll to recharts-wrapper
5755
await page.evaluate(() => {
5856
document.querySelector('.recharts-wrapper')?.scrollIntoView()
5957
})
60-
await eyes.check(
61-
'dashboard page - recharts',
62-
Target.window().layoutRegions('.chart', page.getByPlaceholder('DD/MM/YYYY')),
63-
)
58+
await eyes.check('dashboard page - recharts', Target.window().layoutRegions('.chart'))
6459
})
6560

6661
test('front page should look good', async ({ page }) => {
@@ -75,44 +70,44 @@ test.describe('Visual Tests', () => {
7570

7671
test('timeline page should look good', async ({ page }) => {
7772
await page.goto('/timeline')
78-
await eyes.check(
79-
'timeline page',
80-
Target.window().layoutRegion(page.getByPlaceholder('DD/MM/YYYY')),
81-
)
73+
await eyes.check('timeline page', Target.window())
8274
})
8375

8476
test('gaps page should look good', async ({ page }) => {
8577
await page.goto('/gaps')
86-
await eyes.check('gaps page', Target.window().layoutRegion(page.getByPlaceholder('DD/MM/YYYY')))
78+
await eyes.check('gaps page', Target.window())
8779
})
8880

8981
test('gaps_patterns page should look good', async ({ page }) => {
9082
await page.goto('/gaps_patterns')
91-
await eyes.check(
92-
'gaps_patterns page',
93-
Target.window().layoutRegion(page.getByPlaceholder('DD/MM/YYYY')),
94-
)
83+
await eyes.check('gaps_patterns page', Target.window())
9584
})
9685

9786
test('map page should look good', async ({ page }) => {
98-
await page.clock.setFixedTime(new Date('2023-05-01T00:00:00.000Z'))
9987
await page.goto('/map')
10088
await page.locator('.leaflet-marker-icon').first().waitFor({ state: 'visible' })
10189
await page.locator('.ant-spin-dot').first().waitFor({ state: 'hidden' })
10290
await eyes.check(
10391
'map page',
104-
Target.window().layoutRegions(
105-
page.getByPlaceholder('DD/MM/YYYY'),
106-
page.getByText('מיקומי אוטובוסים משעה'),
107-
),
92+
Target.window().layoutRegions(page.getByText('מיקומי אוטובוסים משעה')),
10893
)
10994
})
95+
96+
test('operator page should look good', async ({ page }) => {
97+
await page.goto('/operator')
98+
await page.getByRole('combobox', { name: 'חברה מפעילה' }).click()
99+
await page.getByRole('option', { name: 'אגד' }).click()
100+
await waitForSkeletonsToHide(page)
101+
await eyes.check('operator page', Target.window().layoutRegions('.chart', '.recharts-wrapper'))
102+
})
103+
110104
test('donation modal should look good', async ({ page }) => {
111105
await page.goto('/')
112106
await page.getByLabel('לתרומות').click()
113107
await page.locator('.MuiTypography-root').first().waitFor()
114108
await eyes.check('donation modal', Target.region(page.getByRole('dialog')))
115109
})
110+
116111
test('donation modal should look good in dark mode', async ({ page }) => {
117112
await page.goto('/')
118113
await page.getByLabel('עבור למצב כהה').click()

0 commit comments

Comments
 (0)