1515 * limitations under the License.
1616 */
1717
18- import { test as it , expect } from './pageTest' ;
18+ import { test as it , expect as baseExpect } from './pageTest' ;
19+ import type { Locator } from 'playwright-core' ;
20+
21+ const expect = baseExpect . extend ( {
22+ async toHaveCountError ( locator : Locator , expected : number ) {
23+ try {
24+ await expect ( locator ) . toHaveCount ( expected ) ;
25+ if ( ! expected )
26+ return { pass : true , message : ( ) => 'Locator has expected count of 0' } ;
27+ return {
28+ pass : false ,
29+ message : ( ) => `Querying locator ${ locator . toString ( ) } should throw, but it did not.` ,
30+ } ;
31+ } catch ( e ) {
32+ const message = ( e as Error ) . message ;
33+ try {
34+ expect ( message ) . toContain ( `"_react" selector is not supported` ) ;
35+ expect ( message ) . toContain ( `resolved to ${ expected } element` ) ;
36+ } catch ( error ) {
37+ return { pass : false , message : ( ) => ( error as Error ) . message } ;
38+ }
39+ return { pass : true , message : ( ) => 'Error message is as expected' } ;
40+ }
41+ }
42+ } ) ;
1943
2044const reacts = {
2145 'react15' : '/reading-list/react15.html' ,
@@ -31,139 +55,138 @@ for (const [name, url] of Object.entries(reacts)) {
3155 } ) ;
3256
3357 it ( 'should work with single-root elements @smoke' , async ( { page } ) => {
34- await expect ( page . locator ( `_react=BookList` ) ) . toHaveCount ( 1 ) ;
35- await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCount ( 3 ) ;
36- await expect ( page . locator ( `_react=BookList >> _react=BookItem` ) ) . toHaveCount ( 3 ) ;
37- await expect ( page . locator ( `_react=BookItem >> _react=BookList` ) ) . toHaveCount ( 0 ) ;
58+ await expect ( page . locator ( `_react=BookList` ) ) . toHaveCountError ( 1 ) ;
59+ await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCountError ( 3 ) ;
60+ await expect ( page . locator ( `_react=BookList >> _react=BookItem` ) ) . toHaveCountError ( 3 ) ;
61+ await expect ( page . locator ( `_react=BookItem >> _react=BookList` ) ) . toHaveCountError ( 0 ) ;
3862 } ) ;
3963
4064 it ( 'should work with multi-root elements (fragments)' , async ( { page } ) => {
4165 it . skip ( name === 'react15' , 'React 15 does not support fragments' ) ;
42- await expect ( page . locator ( `_react=App` ) ) . toHaveCount ( 15 ) ;
43- await expect ( page . locator ( `_react=AppHeader` ) ) . toHaveCount ( 2 ) ;
44- await expect ( page . locator ( `_react=NewBook` ) ) . toHaveCount ( 2 ) ;
66+ await expect ( page . locator ( `_react=App` ) ) . toHaveCountError ( 15 ) ;
67+ await expect ( page . locator ( `_react=AppHeader` ) ) . toHaveCountError ( 2 ) ;
68+ await expect ( page . locator ( `_react=NewBook` ) ) . toHaveCountError ( 2 ) ;
4569 } ) ;
4670
4771 it ( 'should not crash when there is no match' , async ( { page } ) => {
48- await expect ( page . locator ( `_react=Apps` ) ) . toHaveCount ( 0 ) ;
49- await expect ( page . locator ( `_react=BookLi` ) ) . toHaveCount ( 0 ) ;
72+ await expect ( page . locator ( `_react=Apps` ) ) . toHaveCountError ( 0 ) ;
73+ await expect ( page . locator ( `_react=BookLi` ) ) . toHaveCountError ( 0 ) ;
5074 } ) ;
5175
5276 it ( 'should compose' , async ( { page } ) => {
53- await expect ( page . locator ( `_react=NewBook >> _react=button` ) ) . toHaveText ( 'new book' ) ;
54- expect ( await page . $eval ( `_react=NewBook >> _react=input` , el => el . tagName ) ) . toBe ( 'INPUT' ) ;
55- await expect ( page . locator ( `_react=BookItem >> text=Gatsby` ) ) . toHaveText ( 'The Great Gatsby' ) ;
77+ await expect ( page . locator ( `_react=NewBook >> _react=button` ) . locator ( ':scope:has-text("new book")' ) ) . toHaveCountError ( 1 ) ;
78+ await expect ( page . locator ( `_react=BookItem >> text=Gatsby` ) . locator ( ':scope:has-text("The Great Gatsby")' ) ) . toHaveCountError ( 1 ) ;
5679 } ) ;
5780
5881 it ( 'should query by props combinations' , async ( { page } ) => {
59- await expect ( page . locator ( `_react=BookItem[name="The Great Gatsby"]` ) ) . toHaveCount ( 1 ) ;
60- await expect ( page . locator ( `_react=BookItem[name="the great gatsby" i]` ) ) . toHaveCount ( 1 ) ;
61- await expect ( page . locator ( `_react=li[key="The Great Gatsby"]` ) ) . toHaveCount ( 1 ) ;
62- await expect ( page . locator ( `_react=ColorButton[nested.index = 0]` ) ) . toHaveCount ( 1 ) ;
63- await expect ( page . locator ( `_react=ColorButton[nested.nonexisting.index = 0]` ) ) . toHaveCount ( 0 ) ;
64- await expect ( page . locator ( `_react=ColorButton[nested.index.nonexisting = 0]` ) ) . toHaveCount ( 0 ) ;
65- await expect ( page . locator ( `_react=ColorButton[nested.index.nonexisting = 1]` ) ) . toHaveCount ( 0 ) ;
66- await expect ( page . locator ( `_react=ColorButton[nested.value = 4.1]` ) ) . toHaveCount ( 1 ) ;
67- await expect ( page . locator ( `_react=ColorButton[enabled = false]` ) ) . toHaveCount ( 4 ) ;
68- await expect ( page . locator ( `_react=ColorButton[enabled = true] ` ) ) . toHaveCount ( 5 ) ;
69- await expect ( page . locator ( `_react=ColorButton[enabled = true][color = "red"]` ) ) . toHaveCount ( 2 ) ;
70- await expect ( page . locator ( `_react=ColorButton[enabled = true][color = "red"i][nested.index = 6]` ) ) . toHaveCount ( 1 ) ;
82+ await expect ( page . locator ( `_react=BookItem[name="The Great Gatsby"]` ) ) . toHaveCountError ( 1 ) ;
83+ await expect ( page . locator ( `_react=BookItem[name="the great gatsby" i]` ) ) . toHaveCountError ( 1 ) ;
84+ await expect ( page . locator ( `_react=li[key="The Great Gatsby"]` ) ) . toHaveCountError ( 1 ) ;
85+ await expect ( page . locator ( `_react=ColorButton[nested.index = 0]` ) ) . toHaveCountError ( 1 ) ;
86+ await expect ( page . locator ( `_react=ColorButton[nested.nonexisting.index = 0]` ) ) . toHaveCountError ( 0 ) ;
87+ await expect ( page . locator ( `_react=ColorButton[nested.index.nonexisting = 0]` ) ) . toHaveCountError ( 0 ) ;
88+ await expect ( page . locator ( `_react=ColorButton[nested.index.nonexisting = 1]` ) ) . toHaveCountError ( 0 ) ;
89+ await expect ( page . locator ( `_react=ColorButton[nested.value = 4.1]` ) ) . toHaveCountError ( 1 ) ;
90+ await expect ( page . locator ( `_react=ColorButton[enabled = false]` ) ) . toHaveCountError ( 4 ) ;
91+ await expect ( page . locator ( `_react=ColorButton[enabled = true] ` ) ) . toHaveCountError ( 5 ) ;
92+ await expect ( page . locator ( `_react=ColorButton[enabled = true][color = "red"]` ) ) . toHaveCountError ( 2 ) ;
93+ await expect ( page . locator ( `_react=ColorButton[enabled = true][color = "red"i][nested.index = 6]` ) ) . toHaveCountError ( 1 ) ;
7194 } ) ;
7295
7396 it ( 'should exact match by props' , async ( { page } ) => {
74- await expect ( page . locator ( `_react=BookItem[name = "The Great Gatsby"]` ) ) . toHaveText ( ' The Great Gatsby' ) ;
75- await expect ( page . locator ( `_react=BookItem[name = "The Great Gatsby"]` ) ) . toHaveCount ( 1 ) ;
97+ await expect ( page . locator ( `_react=BookItem[name = "The Great Gatsby"]` ) . locator ( ':scope:has-text(" The Great Gatsby")' ) ) . toHaveCountError ( 1 ) ;
98+ await expect ( page . locator ( `_react=BookItem[name = "The Great Gatsby"]` ) ) . toHaveCountError ( 1 ) ;
7699 // case sensitive by default
77- await expect ( page . locator ( `_react=BookItem[name = "the great gatsby"]` ) ) . toHaveCount ( 0 ) ;
78- await expect ( page . locator ( `_react=BookItem[name = "the great gatsby" s]` ) ) . toHaveCount ( 0 ) ;
79- await expect ( page . locator ( `_react=BookItem[name = "the great gatsby" S]` ) ) . toHaveCount ( 0 ) ;
100+ await expect ( page . locator ( `_react=BookItem[name = "the great gatsby"]` ) ) . toHaveCountError ( 0 ) ;
101+ await expect ( page . locator ( `_react=BookItem[name = "the great gatsby" s]` ) ) . toHaveCountError ( 0 ) ;
102+ await expect ( page . locator ( `_react=BookItem[name = "the great gatsby" S]` ) ) . toHaveCountError ( 0 ) ;
80103 // case insensitive with flag
81- await expect ( page . locator ( `_react=BookItem[name = "the great gatsby" i]` ) ) . toHaveCount ( 1 ) ;
82- await expect ( page . locator ( `_react=BookItem[name = "the great gatsby" I]` ) ) . toHaveCount ( 1 ) ;
83- await expect ( page . locator ( `_react=BookItem[name = " The Great Gatsby "]` ) ) . toHaveCount ( 0 ) ;
104+ await expect ( page . locator ( `_react=BookItem[name = "the great gatsby" i]` ) ) . toHaveCountError ( 1 ) ;
105+ await expect ( page . locator ( `_react=BookItem[name = "the great gatsby" I]` ) ) . toHaveCountError ( 1 ) ;
106+ await expect ( page . locator ( `_react=BookItem[name = " The Great Gatsby "]` ) ) . toHaveCountError ( 0 ) ;
84107 } ) ;
85108
86109 it ( 'should partially match by props' , async ( { page } ) => {
87110 // Check partial matching
88- await expect ( page . locator ( `_react=BookItem[name *= "Gatsby"]` ) ) . toHaveText ( ' The Great Gatsby' ) ;
89- await expect ( page . locator ( `_react=BookItem[name *= "Gatsby"]` ) ) . toHaveCount ( 1 ) ;
90- await expect ( page . locator ( `_react=[name *= "Gatsby"]` ) ) . toHaveCount ( 1 ) ;
111+ await expect ( page . locator ( `_react=BookItem[name *= "Gatsby"]` ) . locator ( ':scope:has-text(" The Great Gatsby")' ) ) . toHaveCountError ( 1 ) ;
112+ await expect ( page . locator ( `_react=BookItem[name *= "Gatsby"]` ) ) . toHaveCountError ( 1 ) ;
113+ await expect ( page . locator ( `_react=[name *= "Gatsby"]` ) ) . toHaveCountError ( 1 ) ;
91114
92- await expect ( page . locator ( `_react=BookItem[name = "Gatsby"]` ) ) . toHaveCount ( 0 ) ;
115+ await expect ( page . locator ( `_react=BookItem[name = "Gatsby"]` ) ) . toHaveCountError ( 0 ) ;
93116 } ) ;
94117
95118 it ( 'should support all string operators' , async ( { page } ) => {
96- await expect ( page . locator ( `_react=ColorButton[color = "red"]` ) ) . toHaveCount ( 3 ) ;
97- await expect ( page . locator ( `_react=ColorButton[color |= "red"]` ) ) . toHaveCount ( 3 ) ;
98- await expect ( page . locator ( `_react=ColorButton[color $= "ed"]` ) ) . toHaveCount ( 3 ) ;
99- await expect ( page . locator ( `_react=ColorButton[color ^= "gr"]` ) ) . toHaveCount ( 3 ) ;
100- await expect ( page . locator ( `_react=ColorButton[color ~= "e"]` ) ) . toHaveCount ( 0 ) ;
101- await expect ( page . locator ( `_react=BookItem[name ~= "gatsby" i]` ) ) . toHaveCount ( 1 ) ;
102- await expect ( page . locator ( `_react=BookItem[name *= " gatsby" i]` ) ) . toHaveCount ( 1 ) ;
119+ await expect ( page . locator ( `_react=ColorButton[color = "red"]` ) ) . toHaveCountError ( 3 ) ;
120+ await expect ( page . locator ( `_react=ColorButton[color |= "red"]` ) ) . toHaveCountError ( 3 ) ;
121+ await expect ( page . locator ( `_react=ColorButton[color $= "ed"]` ) ) . toHaveCountError ( 3 ) ;
122+ await expect ( page . locator ( `_react=ColorButton[color ^= "gr"]` ) ) . toHaveCountError ( 3 ) ;
123+ await expect ( page . locator ( `_react=ColorButton[color ~= "e"]` ) ) . toHaveCountError ( 0 ) ;
124+ await expect ( page . locator ( `_react=BookItem[name ~= "gatsby" i]` ) ) . toHaveCountError ( 1 ) ;
125+ await expect ( page . locator ( `_react=BookItem[name *= " gatsby" i]` ) ) . toHaveCountError ( 1 ) ;
103126 } ) ;
104127
105128 it ( 'should support regex' , async ( { page } ) => {
106- await expect ( page . locator ( `_react=ColorButton[color = /red/]` ) ) . toHaveCount ( 3 ) ;
107- await expect ( page . locator ( `_react=ColorButton[color = /^red$/]` ) ) . toHaveCount ( 3 ) ;
108- await expect ( page . locator ( `_react=ColorButton[color = /RED/i]` ) ) . toHaveCount ( 3 ) ;
109- await expect ( page . locator ( `_react=ColorButton[color = /[pqr]ed/]` ) ) . toHaveCount ( 3 ) ;
110- await expect ( page . locator ( `_react=ColorButton[color = /[pq]ed/]` ) ) . toHaveCount ( 0 ) ;
111- await expect ( page . locator ( `_react=BookItem[name = /gat.by/i]` ) ) . toHaveCount ( 1 ) ;
129+ await expect ( page . locator ( `_react=ColorButton[color = /red/]` ) ) . toHaveCountError ( 3 ) ;
130+ await expect ( page . locator ( `_react=ColorButton[color = /^red$/]` ) ) . toHaveCountError ( 3 ) ;
131+ await expect ( page . locator ( `_react=ColorButton[color = /RED/i]` ) ) . toHaveCountError ( 3 ) ;
132+ await expect ( page . locator ( `_react=ColorButton[color = /[pqr]ed/]` ) ) . toHaveCountError ( 3 ) ;
133+ await expect ( page . locator ( `_react=ColorButton[color = /[pq]ed/]` ) ) . toHaveCountError ( 0 ) ;
134+ await expect ( page . locator ( `_react=BookItem[name = /gat.by/i]` ) ) . toHaveCountError ( 1 ) ;
112135 } ) ;
113136
114137 it ( 'should support truthy querying' , async ( { page } ) => {
115- await expect ( page . locator ( `_react=ColorButton[enabled]` ) ) . toHaveCount ( 5 ) ;
138+ await expect ( page . locator ( `_react=ColorButton[enabled]` ) ) . toHaveCountError ( 5 ) ;
116139 } ) ;
117140
118141 it ( 'should support nested react trees' , async ( { page } ) => {
119- await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCount ( 3 ) ;
142+ await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCountError ( 3 ) ;
120143 await page . evaluate ( ( ) => {
121144 // @ts -ignore
122145 mountNestedApp ( ) ;
123146 } ) ;
124- await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCount ( 6 ) ;
147+ await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCountError ( 6 ) ;
125148 } ) ;
126149
127150 it ( 'should work with react memo' , async ( { page } ) => {
128151 it . skip ( name === 'react15' || name === 'react16' , 'Class components dont support memo' ) ;
129- await expect ( page . locator ( `_react=ButtonGrid` ) ) . toHaveCount ( 9 ) ;
152+ await expect ( page . locator ( `_react=ButtonGrid` ) ) . toHaveCountError ( 9 ) ;
130153 } ) ;
131154
132155 it ( 'should work with multiroot react' , async ( { page } ) => {
133156 await it . step ( 'mount second root' , async ( ) => {
134- await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCount ( 3 ) ;
157+ await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCountError ( 3 ) ;
135158 await page . evaluate ( ( ) => {
136159 const anotherRoot = document . createElement ( 'div' ) ;
137160 anotherRoot . id = 'root2' ;
138161 document . body . append ( anotherRoot ) ;
139162 // @ts -ignore
140163 window . mountApp ( anotherRoot ) ;
141164 } ) ;
142- await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCount ( 6 ) ;
165+ await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCountError ( 6 ) ;
143166 } ) ;
144167
145168 await it . step ( 'add a new book to second root' , async ( ) => {
146169 await page . locator ( '#root2 input' ) . fill ( 'newbook' ) ;
147170 await page . locator ( '#root2 >> text=new book' ) . click ( ) ;
148- await expect ( page . locator ( 'css=#root >> _react=BookItem' ) ) . toHaveCount ( 3 ) ;
149- await expect ( page . locator ( 'css=#root2 >> _react=BookItem' ) ) . toHaveCount ( 4 ) ;
171+ await expect ( page . locator ( 'css=#root >> _react=BookItem' ) ) . toHaveCountError ( 3 ) ;
172+ await expect ( page . locator ( 'css=#root2 >> _react=BookItem' ) ) . toHaveCountError ( 4 ) ;
150173 } ) ;
151174 } ) ;
152175
153176 it ( 'should work with multiroot react inside shadow DOM' , async ( { page } ) => {
154- await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCount ( 3 ) ;
177+ await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCountError ( 3 ) ;
155178 await page . evaluate ( ( ) => {
156179 const anotherRoot = document . createElement ( 'div' ) ;
157180 document . body . append ( anotherRoot ) ;
158181 const shadowRoot = anotherRoot . attachShadow ( { mode : 'open' } ) ;
159182 // @ts -ignore
160183 window . mountApp ( shadowRoot ) ;
161184 } ) ;
162- await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCount ( 6 ) ;
185+ await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCountError ( 6 ) ;
163186 } ) ;
164187
165188 it ( 'should work with multiroot react after unmount' , async ( { page } ) => {
166- await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCount ( 3 ) ;
189+ await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCountError ( 3 ) ;
167190
168191 await page . evaluate ( ( ) => {
169192 const anotherRoot = document . createElement ( 'div' ) ;
@@ -172,7 +195,7 @@ for (const [name, url] of Object.entries(reacts)) {
172195 const newRoot = window . mountApp ( anotherRoot ) ;
173196 newRoot . unmount ( ) ;
174197 } ) ;
175- await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCount ( 3 ) ;
198+ await expect ( page . locator ( `_react=BookItem` ) ) . toHaveCountError ( 3 ) ;
176199 } ) ;
177200 } ) ;
178201}
0 commit comments