diff --git a/docs/react.md b/docs/react.md index 98581154f..f0044f587 100644 --- a/docs/react.md +++ b/docs/react.md @@ -66,4 +66,5 @@ To find React element names and props in a tree use [React DevTools](https://chr > Turn off minification for application builds otherwise component names will be uglified as well -React locators work via [resq](https://github.com/baruchvlz/resq) library, which handles React 16 and above. +- With WebDriver and Puppeteer, React locators work via [resq](https://github.com/baruchvlz/resq) library, which handles React 16 and above. +- With Playwright, React locators work via [Playwright React Locator](https://playwright.dev/docs/other-locators#react-locator). diff --git a/docs/vue.md b/docs/vue.md index fb3429c73..e09f921f2 100644 --- a/docs/vue.md +++ b/docs/vue.md @@ -104,6 +104,28 @@ tests If you agreed to create a demo component, you will also see `TestMe` component in `src/components` folder. +## Locators + +For Vue apps a special `vue` locator is available. It allows to select an element by its component name, and props. + +```js +{ vue: 'MyComponent' } +{ vue: 'Button', props: { title: 'Click Me' }} +``` + +With Playwright, you can use Vue locators in any method where locator is required: + +```js +I.click({ vue: 'Tab', props: { title: 'Click Me!' }}); +I.seeElement({ vue: 't', props: { title: 'Clicked' }}); +``` + +To find Vue element names and props in a tree use [Vue DevTools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) extension. + +> Turn off minification for application builds otherwise component names will be uglified as well + +Vue locators work via [Playwright Vue Locator](https://playwright.dev/docs/other-locators#vue-locator). + ## How to write tests? * Open `tests/e2e/app_js` and see the demo test diff --git a/lib/helper/Playwright.js b/lib/helper/Playwright.js index ec8185e2e..390c1b172 100644 --- a/lib/helper/Playwright.js +++ b/lib/helper/Playwright.js @@ -33,7 +33,7 @@ const ElementNotFound = require('./errors/ElementNotFound'); const RemoteBrowserConnectionRefused = require('./errors/RemoteBrowserConnectionRefused'); const Popup = require('./extras/Popup'); const Console = require('./extras/Console'); -const findReact = require('./extras/React'); +const { findReact, findVue } = require('./extras/PlaywrightReactVueLocator'); let playwright; let perfTiming; @@ -3444,6 +3444,7 @@ function buildLocatorString(locator) { async function findElements(matcher, locator) { if (locator.react) return findReact(matcher, locator); + if (locator.vue) return findVue(matcher, locator); locator = new Locator(locator, 'css'); return matcher.locator(buildLocatorString(locator)).all(); @@ -3505,6 +3506,7 @@ async function proceedClick(locator, context = null, options = {}) { async function findClickable(matcher, locator) { if (locator.react) return findReact(matcher, locator); + if (locator.vue) return findVue(matcher, locator); locator = new Locator(locator); if (!locator.isFuzzy()) return findElements.call(this, matcher, locator); diff --git a/lib/helper/extras/PlaywrightReact.js b/lib/helper/extras/PlaywrightReact.js deleted file mode 100644 index 3246122bd..000000000 --- a/lib/helper/extras/PlaywrightReact.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = async function findReact(matcher, locator) { - let _locator = `_react=${locator.react}`; - - if (locator.props && locator.props.name) { - _locator += `[name = "${locator.props.name}"]`; - } - - return matcher.locator(_locator).all(); -}; diff --git a/lib/helper/extras/PlaywrightReactVueLocator.js b/lib/helper/extras/PlaywrightReactVueLocator.js new file mode 100644 index 000000000..59f8a26eb --- /dev/null +++ b/lib/helper/extras/PlaywrightReactVueLocator.js @@ -0,0 +1,38 @@ +async function findReact(matcher, locator) { + let _locator = `_react=${locator.react}`; + let props = ''; + + if (locator.props) { + props += propBuilder(locator.props); + _locator += props; + } + return matcher.locator(_locator).all(); +} + +async function findVue(matcher, locator) { + let _locator = `_vue=${locator.vue}`; + let props = ''; + + if (locator.props) { + props += propBuilder(locator.props); + _locator += props; + } + return matcher.locator(_locator).all(); +} + +function propBuilder(props) { + let _props = ''; + + for (const [key, value] of Object.entries(props)) { + if (typeof value === 'object') { + for (const [k, v] of Object.entries(value)) { + _props += `[${key}.${k} = "${v}"]`; + } + } else { + _props += `[${key} = "${value}"]`; + } + } + return _props; +} + +module.exports = { findReact, findVue }; diff --git a/typings/index.d.ts b/typings/index.d.ts index f6c8b065a..51e92c4f9 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -431,7 +431,7 @@ declare namespace CodeceptJS { | string | ILocator | Locator - | CustomLocators[keyof CustomLocators]; + | CustomLocators; type StringOrSecret = string | CodeceptJS.Secret;