diff --git a/src/api/Page.ts b/src/api/Page.ts index 56a9be6..7d4e4f4 100644 --- a/src/api/Page.ts +++ b/src/api/Page.ts @@ -137,6 +137,23 @@ class Page extends EventEmitter { return result.value } + async focus (selector: string) { + /* istanbul ignore next */ + return this.evaluate((sel) => { + const el = document.querySelector(sel as string) + + if (el === null) { + throw new Error('Unable to find element') + } + + if (!(el instanceof HTMLElement)) { + throw new Error('Found element is not HTMLElement and not focusable') + } + + el.focus() + }, selector) + } + async goto (url: string) { await this._send('WebDriver:Navigate', { url }) } diff --git a/test/api/Page.ts b/test/api/Page.ts index b3182b2..bab6f2f 100644 --- a/test/api/Page.ts +++ b/test/api/Page.ts @@ -209,6 +209,46 @@ test('Page: `evaluate()`', testWithFirefox(async (t) => { } })) +test('Page: `focus()`', testWithFirefox(async (t) => { + const browser = await foxr.connect() + const page = await browser.newPage() + + await page.setContent('
') + + const activeElementBefore = await page.evaluate('document.activeElement.tagName') + + await page.focus('input') + + const activeElementAfter = await page.evaluate('document.activeElement.tagName') + + t.true( + activeElementBefore !== activeElementAfter && activeElementAfter === 'INPUT', + 'should focus element' + ) + + try { + await page.focus('foo') + t.fail() + } catch (err) { + t.equal( + err.message, + 'Evaluation failed: Unable to find element', + 'should throw if there is no such an element' + ) + } + + try { + await page.focus('svg') + t.fail() + } catch (err) { + t.equal( + err.message, + 'Evaluation failed: Found element is not HTMLElement and not focusable', + 'should throw if found element is not focusable' + ) + } +})) + test('Page: `goto()` + `url()`', testWithFirefox(async (t) => { const browser = await foxr.connect() const page = await browser.newPage() diff --git a/tsconfig.json b/tsconfig.json index 59174fe..e68f740 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "allowSyntheticDefaultImports": true, "noEmit": true, "pretty": true, - "lib": ["esnext"], + "lib": ["esnext", "dom"], "moduleResolution": "node", "typeRoots": [ "types/", "node_modules/@types/" ] },