diff --git a/devtools/src/devtools/components/MenuBar.js b/devtools/src/devtools/components/MenuBar.js index 8067cd50..567eac97 100644 --- a/devtools/src/devtools/components/MenuBar.js +++ b/devtools/src/devtools/components/MenuBar.js @@ -37,7 +37,7 @@ function MenuBar({ cssPath, suggestion }) { className="focus:outline-none" title="Inspect the matching DOM element" disabled={!cssPath} - onClick={() => inspectedWindow.inspect(cssPath)} + onClick={() => inspectedWindow.inspect(cssPath.toString())} > diff --git a/package-lock.json b/package-lock.json index afd970e9..d166fce5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3270,9 +3270,9 @@ } }, "@testing-library/dom": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.12.0.tgz", - "integrity": "sha512-DaP36Pa7G8cFros2MFQWNQsZSwl1DjG/OYQzFSM6gt1N1bzxsqb1GaxPo6LyTKA622xDC2KqPpqF+PsOlYd00Q==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.15.0.tgz", + "integrity": "sha512-H+cQksHNYjxTS62S+exT5ZcBZeJXE3NDHUKs6MTopp4cMgd8DHX78IUohyUGqJRD1AthtgnKujrPTxYdWZ/w9w==", "requires": { "@babel/runtime": "^7.10.2", "aria-query": "^4.0.2", diff --git a/package.json b/package.json index c48d1f7f..8e0e6072 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "postversion": "git push && git push --tags && git checkout master && git merge develop --ff && git push && git checkout -" }, "dependencies": { - "@testing-library/dom": "^7.12.0", + "@testing-library/dom": "^7.15.0", "codemirror": "5.54.0", "crx-bridge": "^2.1.0", "deep-diff": "^1.0.2", diff --git a/src/components/Result.js b/src/components/Result.js index f771ef83..9cfe0713 100644 --- a/src/components/Result.js +++ b/src/components/Result.js @@ -3,7 +3,6 @@ import ErrorBox from './ErrorBox'; import ResultQueries from './ResultQueries'; import ResultSuggestion from './ResultSuggestion'; import Scrollable from './Scrollable'; -import { emptyResult } from '../lib'; function Result({ result, dispatch }) { if (result.error) { @@ -37,9 +36,7 @@ function Result({ result, dispatch }) { ); } - - const { data, suggestion } = result.elements?.[0] || emptyResult; - + const { data, suggestion, queries } = result.elements[0]; return (
@@ -55,6 +52,7 @@ function Result({ result, dispatch }) { {children}
; } function Heading({ children }) { - return

{children}

; + return

{children}

; } -const Field = React.memo(function Field({ method, data, dispatch, active }) { +const Field = React.memo(function Field({ + data, + method, + query, + dispatch, + active, +}) { const field = getFieldName(method); const value = data[field]; - const handleClick = value ? () => { - const expression = getExpression({ method, data }); - dispatch({ type: 'SET_QUERY', query: expression }); + dispatch({ + type: 'SET_QUERY', + query: `screen.${query.toString()}`, + }); } : undefined; @@ -26,7 +34,7 @@ const Field = React.memo(function Field({ method, data, dispatch, active }) { data-clickable={!!handleClick} onClick={handleClick} > -
{field}
+
{field}
{value || n/a}
@@ -35,68 +43,62 @@ const Field = React.memo(function Field({ method, data, dispatch, active }) { }); // for inputs, the role will only work if there is also a type attribute -function ResultQueries({ data, dispatch, activeMethod }) { +function ResultQueries({ data, possibleQueries, dispatch, activeMethod }) { return (
1. Queries Accessible to Everyone - - - - - + {queries + .filter((query) => query.type === 'ACCESSIBLE') + .map((query) => { + return ( + + ); + })}
2. Semantic Queries - - + {queries + .filter((query) => query.type === 'SEMANTIC') + .map((query) => { + return ( + + ); + })}
3. TestId - + {queries + .filter((query) => query.type === 'TEST') + .map((query) => { + return ( + + ); + })}
diff --git a/src/constants.js b/src/constants.js index 710afa21..a7801e49 100644 --- a/src/constants.js +++ b/src/constants.js @@ -25,18 +25,22 @@ screen.getByRole('button') }; export const queries = [ - { method: 'getByRole', level: 0 }, - { method: 'getByLabelText', level: 0 }, - { method: 'getByPlaceholderText', level: 0 }, - { method: 'getByText', level: 0 }, - { method: 'getByDisplayValue', level: 0 }, + { method: 'getByRole', level: 0, type: 'ACCESSIBLE' }, + { method: 'getByLabelText', level: 0, type: 'ACCESSIBLE' }, + { + method: 'getByPlaceholderText', + level: 0, + type: 'ACCESSIBLE', + }, + { method: 'getByText', level: 0, type: 'ACCESSIBLE' }, + { method: 'getByDisplayValue', level: 0, type: 'ACCESSIBLE' }, - { method: 'getByAltText', level: 1 }, - { method: 'getByTitle', level: 1 }, + { method: 'getByAltText', level: 1, type: 'SEMANTIC' }, + { method: 'getByTitle', level: 1, type: 'SEMANTIC' }, - { method: 'getByTestId', level: 2 }, + { method: 'getByTestId', level: 2, type: 'TEST' }, - { method: 'querySelector', level: 3 }, + { method: 'querySelector', level: 3, type: 'GENERIC' }, ]; // some quotes from https://testing-library.com/docs/guide-which-query diff --git a/src/lib/getExpression.js b/src/lib/getExpression.js deleted file mode 100644 index b04d3ee7..00000000 --- a/src/lib/getExpression.js +++ /dev/null @@ -1,41 +0,0 @@ -export function wrapInQuotes(val) { - if (!val.includes(`'`)) { - return `'${val}'`; - } - - if (!val.includes('"')) { - return `"${val}"`; - } - - if (!val.includes('`')) { - return `\`${val}\``; - } - - return `'${val.replace(/'/g, `\\'`)}'`; -} - -export function getFieldName(method) { - return method[5].toLowerCase() + method.substr(6); -} - -export function getExpression({ method, data }) { - const field = getFieldName(method); - - if (method === 'getByRole') { - if (data.role && data.name) { - const matcher = new RegExp(`${data.name}`.toLowerCase(), 'i'); - return `screen.getByRole('${data.role}', { name: ${matcher} })`; - } else { - return `screen.getByRole('${data.role}')`; - } - } - - if (data[field]) { - const matcher = new RegExp(`${data[field]}`.toLowerCase(), 'i'); - return `screen.${method}(${matcher})`; - } - - return ''; -} - -export default getExpression; diff --git a/src/lib/getFieldName.js b/src/lib/getFieldName.js new file mode 100644 index 00000000..c01942ab --- /dev/null +++ b/src/lib/getFieldName.js @@ -0,0 +1,19 @@ +export function wrapInQuotes(val) { + if (!val.includes(`'`)) { + return `'${val}'`; + } + + if (!val.includes('"')) { + return `"${val}"`; + } + + if (!val.includes('`')) { + return `\`${val}\``; + } + + return `'${val.replace(/'/g, `\\'`)}'`; +} + +export function getFieldName(method) { + return method[5].toLowerCase() + method.substr(6); +} diff --git a/src/lib/index.js b/src/lib/index.js index 9369f952..8801cc40 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -1,3 +1,3 @@ export * from './ensureArray'; -export * from './getExpression'; +export * from './getFieldName'; export * from './queryAdvise'; diff --git a/src/lib/queryAdvise.js b/src/lib/queryAdvise.js index 34bbe091..88e5ea8e 100644 --- a/src/lib/queryAdvise.js +++ b/src/lib/queryAdvise.js @@ -2,6 +2,7 @@ import { messages, queries } from '../constants'; import { computeAccessibleName, getRole } from 'dom-accessibility-api'; import { getSuggestedQuery } from '@testing-library/dom'; import cssPath from './cssPath'; +import { getFieldName } from './'; export function getData({ rootNode, element }) { const type = element.getAttribute('type'); @@ -80,3 +81,19 @@ export function getQueryAdvise({ rootNode, element }) { suggestion, }; } + +export function getAllPossibileQueries(element) { + const possibleQueries = queries + .filter((query) => query.type !== 'GENERIC') + .map((query) => { + const method = getFieldName(query.method); + return getSuggestedQuery(element, 'get', method); + }) + .filter((suggestedQuery) => suggestedQuery !== undefined) + .reduce((obj, suggestedQuery) => { + obj[suggestedQuery.queryMethod] = suggestedQuery; + return obj; + }, {}); + + return possibleQueries; +} diff --git a/src/lib/queryAdvise.test.js b/src/lib/queryAdvise.test.js index 353c8f6c..993832e0 100644 --- a/src/lib/queryAdvise.test.js +++ b/src/lib/queryAdvise.test.js @@ -1,4 +1,4 @@ -import { getQueryAdvise } from './queryAdvise'; +import { getQueryAdvise, getAllPossibileQueries } from './queryAdvise'; const emptyObject = ` Object { @@ -48,3 +48,111 @@ it('should add `screen.` on suggested query returned by getSuggestedQuery', () = const result = getQueryAdvise({ rootNode, element }); expect(result.suggestion.expression).toStartWith('screen.'); }); + +it('[getAllPossibileQueries] should return an object with all possibile queries', () => { + const rootNode = document.createElement('div'); + const element = document.createElement('button'); + rootNode.appendChild(element); + let suggestedQueries = getAllPossibileQueries(element); + + expect(suggestedQueries).toMatchInlineSnapshot(` + Object { + "getByRole": Object { + "queryArgs": Array [ + "button", + ], + "queryMethod": "getByRole", + "queryName": "Role", + "toString": [Function], + "variant": "get", + }, + } + `); + + rootNode.innerHTML = ` + + + `; + const input = rootNode.querySelector('input'); + suggestedQueries = getAllPossibileQueries(input); + + expect(suggestedQueries).toMatchInlineSnapshot(` + Object { + "getByAltText": Object { + "queryArgs": Array [ + /enter your username/i, + ], + "queryMethod": "getByAltText", + "queryName": "AltText", + "toString": [Function], + "variant": "get", + }, + "getByDisplayValue": Object { + "queryArgs": Array [ + /john-doe/i, + ], + "queryMethod": "getByDisplayValue", + "queryName": "DisplayValue", + "toString": [Function], + "variant": "get", + }, + "getByLabelText": Object { + "queryArgs": Array [ + /username/i, + ], + "queryMethod": "getByLabelText", + "queryName": "LabelText", + "toString": [Function], + "variant": "get", + }, + "getByPlaceholderText": Object { + "queryArgs": Array [ + /how should i call you\\?/i, + ], + "queryMethod": "getByPlaceholderText", + "queryName": "PlaceholderText", + "toString": [Function], + "variant": "get", + }, + "getByRole": Object { + "queryArgs": Array [ + "textbox", + Object { + "name": /username/i, + }, + ], + "queryMethod": "getByRole", + "queryName": "Role", + "toString": [Function], + "variant": "get", + }, + "getByTestId": Object { + "queryArgs": Array [ + "uname", + ], + "queryMethod": "getByTestId", + "queryName": "TestId", + "toString": [Function], + "variant": "get", + }, + "getByTitle": Object { + "queryArgs": Array [ + /enter your username/i, + ], + "queryMethod": "getByTitle", + "queryName": "Title", + "toString": [Function], + "variant": "get", + }, + } + `); +}); diff --git a/src/parser.js b/src/parser.js index e23d4cef..51b67f04 100644 --- a/src/parser.js +++ b/src/parser.js @@ -3,6 +3,7 @@ import { ensureArray, getQueryAdvise } from './lib'; import { queries as supportedQueries } from './constants'; import cssPath from './lib/cssPath'; import deepEqual from './lib/deepEqual'; +import { getAllPossibileQueries } from './lib/queryAdvise'; import { getQueriesForElement, @@ -123,6 +124,7 @@ function createEvaluator({ rootNode }) { data, target: element, cssPath: cssPath(element, true), + queries: getAllPossibileQueries(element), }; });