Skip to content

Commit

Permalink
assertVue*() methods support Vue 3 composition API (#969)
Browse files Browse the repository at this point in the history
If Vue's composition API used, reactive data and computed props cannot
be inspected through these methods.

When fetching a component prop, attempt these paths:

1. el.__vue__.key
   * Vue 2
2. el.__vueParentComponent.ctx.key
   * Vue 3 options API
   * Vue 3 composition API for definedProps()
3. el.__vueParentComponent.setupState.key
   * Vue 3 composition API reactive(), computed(), or any other
     <script setup> variables.

If the ctx key is undefined, the setupState fallback is attempted.
Wrap in a try/catch for attempts to access undefined nested props which
could throw an error.
  • Loading branch information
derekmd authored Mar 24, 2022
1 parent 4287a12 commit f62afe1
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 14 deletions.
11 changes: 8 additions & 3 deletions src/Concerns/MakesAssertions.php
Original file line number Diff line number Diff line change
Expand Up @@ -1102,9 +1102,14 @@ public function vueAttribute($componentSelector, $key)

return $this->driver->executeScript(
"var el = document.querySelector('".$fullSelector."');".
"return typeof el.__vue__ === 'undefined' ".
'? JSON.parse(JSON.stringify(el.__vueParentComponent.ctx)).'.$key.
': el.__vue__.'.$key
"if (typeof el.__vue__ !== 'undefined')".
' return el.__vue.'.$key.';'.
'try {'.
' var attr = el.__vueParentComponent.ctx.'.$key.';'.
" if (typeof attr !== 'undefined')".
' return attr;'.
'} catch (e) {}'.
'return el.__vueParentComponent.setupState.'.$key.';'
);
}
}
37 changes: 26 additions & 11 deletions tests/MakesAssertionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1018,10 +1018,15 @@ public function test_assert_vue()
$driver = m::mock(stdClass::class);
$driver->shouldReceive('executeScript')
->with(
'var el = document.querySelector(\'body foo\');'.
"return typeof el.__vue__ === 'undefined' ".
'? JSON.parse(JSON.stringify(el.__vueParentComponent.ctx)).foo'.
': el.__vue__.foo'
"var el = document.querySelector('body foo');".
"if (typeof el.__vue__ !== 'undefined')".
' return el.__vue.foo;'.
'try {'.
' var attr = el.__vueParentComponent.ctx.foo;'.
" if (typeof attr !== 'undefined')".
' return attr;'.
'} catch (e) {}'.
'return el.__vueParentComponent.setupState.foo;'
)
->twice()
->andReturn('foo');
Expand Down Expand Up @@ -1071,10 +1076,15 @@ public function test_assert_vue_is_not()
$driver = m::mock(stdClass::class);
$driver->shouldReceive('executeScript')
->with(
'var el = document.querySelector(\'body foo\');'.
"return typeof el.__vue__ === 'undefined' ".
'? JSON.parse(JSON.stringify(el.__vueParentComponent.ctx)).foo'.
': el.__vue__.foo'
"var el = document.querySelector('body foo');".
"if (typeof el.__vue__ !== 'undefined')".
' return el.__vue.foo;'.
'try {'.
' var attr = el.__vueParentComponent.ctx.foo;'.
" if (typeof attr !== 'undefined')".
' return attr;'.
'} catch (e) {}'.
'return el.__vueParentComponent.setupState.foo;'
)
->twice()
->andReturn('foo');
Expand Down Expand Up @@ -1125,9 +1135,14 @@ public function test_assert_vue_contains_formats_vue_prop_query()
$driver->shouldReceive('executeScript')
->with(
'var el = document.querySelector(\'body [dusk="vue-component"]\');'.
"return typeof el.__vue__ === 'undefined' ".
'? JSON.parse(JSON.stringify(el.__vueParentComponent.ctx)).name'.
': el.__vue__.name'
"if (typeof el.__vue__ !== 'undefined')".
' return el.__vue.name;'.
'try {'.
' var attr = el.__vueParentComponent.ctx.name;'.
" if (typeof attr !== 'undefined')".
' return attr;'.
'} catch (e) {}'.
'return el.__vueParentComponent.setupState.name;'
)
->once()
->andReturn(['john']);
Expand Down

0 comments on commit f62afe1

Please sign in to comment.