1
1
import { importLocale } from '@node-core/website-i18n' ;
2
2
import { test , expect , type Page } from '@playwright/test' ;
3
3
4
+ const englishLocale = await importLocale ( 'en' ) ;
5
+
4
6
// TODO(@avivkeller): It would be ideal for all the Test IDs to not exist in the
5
7
// ui-components package, and instead be passed as props.
6
- const testIds = {
7
- themeToggle : 'theme-toggle' ,
8
- languageDropdown : 'language-selector' ,
9
- languageOptions : 'language-options' ,
10
- navLinks : 'nav-links' ,
11
- mobileMenuToggle : 'mobile-menu-toggle' ,
12
- } ;
13
-
14
- // These are inherited from Orama, so they don't have test IDs. Instead, we use the element names directly
15
- const selectors = {
16
- searchButton : 'orama-button' ,
17
- searchInput : 'orama-input' ,
18
- searchResults : 'orama-search-results' ,
8
+ const locators = {
9
+ // Navigation elements
10
+ navLinksTestID : 'nav-links' ,
11
+ mobileMenuToggleName :
12
+ englishLocale . components . containers . navBar . controls . toggle ,
13
+
14
+ // Global UI controls
15
+ languageDropdownName : englishLocale . components . common . languageDropdown . label ,
16
+ themeToggleName : englishLocale . components . common . themeToggle . label ,
17
+
18
+ // Search components (from Orama library)
19
+ searchButtonTag : 'orama-button' ,
20
+ searchInputTag : 'orama-input' ,
21
+ searchResultsTag : 'orama-search-results' ,
19
22
} ;
20
23
21
- // Helper functions
22
24
const getTheme = ( page : Page ) =>
23
25
page . evaluate ( ( ) => document . documentElement . dataset . theme ) ;
24
26
25
27
const openLanguageMenu = async ( page : Page ) => {
26
- await page . getByTestId ( testIds . languageDropdown ) . first ( ) . click ( ) ;
27
- await page . waitForSelector ( `data-testid=${ testIds . languageOptions } ` ) ;
28
- } ;
28
+ const button = page . getByRole ( 'button' , {
29
+ name : locators . languageDropdownName ,
30
+ } ) ;
31
+ const selector = `[aria-labelledby=${ await button . getAttribute ( 'id' ) } ]` ;
32
+ await button . click ( ) ;
29
33
30
- const verifyTranslation = async ( page : Page , locale : string ) => {
31
- const localeData = await importLocale ( locale ) ;
34
+ await page . waitForSelector ( selector ) ;
35
+ return page . locator ( selector ) ;
36
+ } ;
32
37
33
- // Get all navigation links
34
- const links = await page . getByTestId ( testIds . navLinks ) . locator ( 'a' ) . all ( ) ;
38
+ const verifyTranslation = async (
39
+ page : Page ,
40
+ locale : string | Record < string , unknown >
41
+ ) => {
42
+ // Load locale data if string code provided (e.g., 'es', 'fr')
43
+ const localeData =
44
+ typeof locale === 'string' ? await importLocale ( locale ) : locale ;
45
+
46
+ // Get navigation links and expected translations
47
+ const links = await page
48
+ . getByTestId ( locators . navLinksTestID )
49
+ . locator ( 'a' )
50
+ . all ( ) ;
35
51
const expectedTexts = Object . values (
36
52
localeData . components . containers . navBar . links
37
53
) ;
38
54
39
- // For each link, verify its text is in the expected translations
55
+ // Verify each navigation link text matches an expected translation
40
56
for ( const link of links ) {
41
57
const linkText = await link . textContent ( ) ;
42
58
expect ( expectedTexts ) . toContain ( linkText ! . trim ( ) ) ;
43
59
}
44
60
} ;
45
61
46
62
test . describe ( 'Node.js Website' , ( ) => {
63
+ // Start each test from the English homepage
47
64
test . beforeEach ( async ( { page } ) => {
48
65
await page . goto ( '/en' ) ;
49
66
} ) ;
50
67
51
68
test . describe ( 'Theme' , ( ) => {
52
69
test ( 'should toggle between light/dark themes' , async ( { page } ) => {
53
- const themeToggle = page . getByTestId ( testIds . themeToggle ) . first ( ) ;
70
+ const themeToggle = page . getByRole ( 'button' , {
71
+ name : locators . themeToggleName ,
72
+ } ) ;
54
73
await expect ( themeToggle ) . toBeVisible ( ) ;
55
74
56
75
const initialTheme = await getTheme ( page ) ;
@@ -62,12 +81,13 @@ test.describe('Node.js Website', () => {
62
81
} ) ;
63
82
64
83
test ( 'should persist theme across page navigation' , async ( { page } ) => {
65
- const themeToggle = page . getByTestId ( testIds . themeToggle ) . first ( ) ;
84
+ const themeToggle = page . getByRole ( 'button' , {
85
+ name : locators . themeToggleName ,
86
+ } ) ;
66
87
await themeToggle . click ( ) ;
67
88
const selectedTheme = await getTheme ( page ) ;
68
89
69
90
await page . reload ( ) ;
70
-
71
91
expect ( await getTheme ( page ) ) . toBe ( selectedTheme ) ;
72
92
} ) ;
73
93
@@ -86,15 +106,11 @@ test.describe('Node.js Website', () => {
86
106
test ( 'should correctly translate UI elements according to language files' , async ( {
87
107
page,
88
108
} ) => {
89
- // Verify English content
90
- await verifyTranslation ( page , 'en' ) ;
91
-
92
- // Change to Spanish and verify
93
- await openLanguageMenu ( page ) ;
94
- await page
95
- . getByTestId ( testIds . languageOptions )
96
- . getByText ( / e s p a ñ o l / i)
97
- . click ( ) ;
109
+ await verifyTranslation ( page , englishLocale ) ;
110
+
111
+ // Change to Spanish and verify translations
112
+ const menu = await openLanguageMenu ( page ) ;
113
+ await menu . getByText ( / e s p a ñ o l / i) . click ( ) ;
98
114
await page . waitForURL ( / \/ e s $ / ) ;
99
115
100
116
await verifyTranslation ( page , 'es' ) ;
@@ -103,34 +119,40 @@ test.describe('Node.js Website', () => {
103
119
104
120
test . describe ( 'Search' , ( ) => {
105
121
test ( 'should show and operate search functionality' , async ( { page } ) => {
106
- await page . locator ( selectors . searchButton ) . click ( ) ;
122
+ // Open search dialog
123
+ await page . locator ( locators . searchButtonTag ) . click ( ) ;
107
124
108
- const searchInput = page . locator ( selectors . searchInput ) ;
125
+ // Verify search input is visible and enter a search term
126
+ const searchInput = page . locator ( locators . searchInputTag ) ;
109
127
await expect ( searchInput ) . toBeVisible ( ) ;
110
128
await searchInput . pressSequentially ( 'express' ) ;
111
129
112
- const searchResults = page . locator ( selectors . searchResults ) ;
130
+ // Verify search results appear
131
+ const searchResults = page . locator ( locators . searchResultsTag ) ;
113
132
await expect ( searchResults ) . toBeVisible ( ) ;
114
133
} ) ;
115
134
} ) ;
116
135
117
- test . describe ( 'Navigation' , ( ) => {
136
+ test . describe . only ( 'Navigation' , ( ) => {
118
137
test ( 'should have functioning mobile menu on small screens' , async ( {
119
138
page,
120
139
} ) => {
121
- // Set mobile viewport
140
+ // Set mobile viewport size
122
141
await page . setViewportSize ( { width : 375 , height : 667 } ) ;
123
142
124
- const mobileToggle = page . getByTestId ( testIds . mobileMenuToggle ) ;
143
+ // Locate mobile menu toggle button and verify it's visible
144
+ const mobileToggle = page . getByRole ( 'button' , {
145
+ name : locators . mobileMenuToggleName ,
146
+ } ) ;
125
147
await expect ( mobileToggle ) . toBeVisible ( ) ;
126
148
127
- const navLinks = page . getByTestId ( testIds . navLinks ) ;
149
+ const navLinks = page . getByTestId ( locators . navLinksTestID ) ;
128
150
129
- // Toggle menu open and verify
151
+ // Toggle menu open and verify it's visible
130
152
await mobileToggle . click ( ) ;
131
153
await expect ( navLinks . first ( ) ) . toBeVisible ( ) ;
132
154
133
- // Toggle menu closed and verify
155
+ // Toggle menu closed and verify it's hidden
134
156
await mobileToggle . click ( ) ;
135
157
await expect ( navLinks . first ( ) ) . not . toBeVisible ( ) ;
136
158
} ) ;
0 commit comments