-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Google password manager] Android isolated module for password import (…
…#1157) * feat: add logic for password import * feat: add logic for password import * chore: add some comments * chore: PR comments * refactor: rename and update config structure * chore: pr comments * chore: remove file --------- Co-authored-by: Jonathan Kingston <[email protected]>
- Loading branch information
1 parent
a44aaef
commit 0454d7d
Showing
11 changed files
with
559 additions
and
5 deletions.
There are no files selected for viewing
118 changes: 118 additions & 0 deletions
118
injected/integration-test/autofill-password-import.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { test } from '@playwright/test' | ||
import { readFileSync } from 'fs' | ||
import { | ||
mockAndroidMessaging, | ||
wrapWebkitScripts | ||
} from '@duckduckgo/messaging/lib/test-utils.mjs' | ||
import { perPlatform } from './type-helpers.mjs' | ||
|
||
test('Password import feature', async ({ page }, testInfo) => { | ||
const passwordImportFeature = AutofillPasswordImportSpec.create(page, testInfo) | ||
await passwordImportFeature.enabled() | ||
await passwordImportFeature.navigate() | ||
const didAnimatePasswordOptions = passwordImportFeature.waitForAnimation('a[aria-label="Password options"]') | ||
await passwordImportFeature.clickOnElement('Home page') | ||
await didAnimatePasswordOptions | ||
|
||
const didAnimateSignin = passwordImportFeature.waitForAnimation('a[aria-label="Sign in"]') | ||
await passwordImportFeature.clickOnElement('Signin page') | ||
await didAnimateSignin | ||
|
||
const didAnimateExport = passwordImportFeature.waitForAnimation('button[aria-label="Export"]') | ||
await passwordImportFeature.clickOnElement('Export page') | ||
await didAnimateExport | ||
}) | ||
|
||
export class AutofillPasswordImportSpec { | ||
htmlPage = '/autofill-password-import/index.html' | ||
config = './integration-test/test-pages/autofill-password-import/config/config.json' | ||
/** | ||
* @param {import("@playwright/test").Page} page | ||
* @param {import("./type-helpers.mjs").Build} build | ||
* @param {import("./type-helpers.mjs").PlatformInfo} platform | ||
*/ | ||
constructor (page, build, platform) { | ||
this.page = page | ||
this.build = build | ||
this.platform = platform | ||
} | ||
|
||
async enabled () { | ||
const config = JSON.parse(readFileSync(this.config, 'utf8')) | ||
await this.setup({ config }) | ||
} | ||
|
||
async navigate () { | ||
await this.page.goto(this.htmlPage) | ||
} | ||
|
||
/** | ||
* @param {object} params | ||
* @param {Record<string, any>} params.config | ||
* @return {Promise<void>} | ||
*/ | ||
async setup (params) { | ||
const { config } = params | ||
|
||
// read the built file from disk and do replacements | ||
const injectedJS = wrapWebkitScripts(this.build.artifact, { | ||
$CONTENT_SCOPE$: config, | ||
$USER_UNPROTECTED_DOMAINS$: [], | ||
$USER_PREFERENCES$: { | ||
platform: { name: 'android' }, | ||
debug: true, | ||
javascriptInterface: '', | ||
messageCallback: '', | ||
sessionKey: '' | ||
} | ||
}) | ||
|
||
await this.page.addInitScript(mockAndroidMessaging, { | ||
messagingContext: { | ||
env: 'development', | ||
context: 'contentScopeScripts', | ||
featureName: 'n/a' | ||
}, | ||
responses: {}, | ||
messageCallback: '' | ||
}) | ||
|
||
// attach the JS | ||
await this.page.addInitScript(injectedJS) | ||
} | ||
|
||
/** | ||
* Helper for creating an instance per platform | ||
* @param {import("@playwright/test").Page} page | ||
* @param {import("@playwright/test").TestInfo} testInfo | ||
*/ | ||
static create (page, testInfo) { | ||
// Read the configuration object to determine which platform we're testing against | ||
const { platformInfo, build } = perPlatform(testInfo.project.use) | ||
return new AutofillPasswordImportSpec(page, build, platformInfo) | ||
} | ||
|
||
/** | ||
* Helper to assert that an element is animating | ||
* @param {string} selector | ||
*/ | ||
async waitForAnimation (selector) { | ||
const locator = await this.page.locator(selector) | ||
return await locator.evaluate((el) => { | ||
if (el != null) { | ||
return el.getAnimations().some((animation) => animation.playState === 'running') | ||
} else { | ||
return false | ||
} | ||
}, selector) | ||
} | ||
|
||
/** | ||
* Helper to click on a button accessed via the aria-label attrbitue | ||
* @param {string} text | ||
*/ | ||
async clickOnElement (text) { | ||
const element = await this.page.getByText(text) | ||
await element.click() | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
injected/integration-test/test-pages/autofill-password-import/config/config.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
{ | ||
"features": { | ||
"autofillPasswordImport": { | ||
"state": "enabled", | ||
"exceptions": [], | ||
"settings": { | ||
"domains": [ | ||
{ | ||
"domain": [ | ||
"passwords.google.com", | ||
"localhost" | ||
], | ||
"patchSettings": [ | ||
{ | ||
"path": "", | ||
"op": "add", | ||
"value": { | ||
"settingsButton": { | ||
"shouldAutotap": false, | ||
"path": "/", | ||
"selectors": [ | ||
"a[href*='options']" | ||
], | ||
"labelTexts": [ | ||
"Password options" | ||
] | ||
}, | ||
"exportButton": { | ||
"shouldAutotap": false, | ||
"path": "/options", | ||
"selectors": [ | ||
"c-wiz[data-node-index*='2;0'][data-p*='options']", | ||
"c-wiz[data-p*='options'][jsdata='deferred-i4']" | ||
], | ||
"labelTexts": [ | ||
"Export" | ||
] | ||
}, | ||
"signInButton": { | ||
"shouldAutotap": false, | ||
"path": "/intro", | ||
"selectors": [ | ||
"a[href*='ServiceLogin']:not([target='_top'])", | ||
"a[aria-label='Sign in']:not([target='_top'])" | ||
], | ||
"labelTexts": [ | ||
"Sign in" | ||
] | ||
} | ||
} | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} | ||
}, | ||
"unprotectedTemporary": [] | ||
} |
75 changes: 75 additions & 0 deletions
75
injected/integration-test/test-pages/autofill-password-import/index.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>A page with SPA navigation (like google password manager)</title> | ||
<p> | ||
This is an SPA like page, with some elements that appear on navigation using the pushState API. | ||
</p> | ||
<style> | ||
.body { | ||
display: flex; | ||
flex-direction: column; | ||
height: 100%; | ||
margin-bottom: 50px; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
.page { | ||
display: none; | ||
} | ||
.page.active { | ||
display: flex; | ||
height: 100%; | ||
justify-content: center; | ||
align-items: center; | ||
} | ||
</style> | ||
</head> | ||
<body class="body"> | ||
<div id="intro" class="page"> | ||
<a aria-label="Sign in">Sign in</a> | ||
</div> | ||
<div id="home" class="page"> | ||
<a aria-label="Password options">Password options</a> | ||
</div> | ||
<div id="options" class="page"> | ||
<button aria-label="Export">Export</button> | ||
</div> | ||
|
||
<script> | ||
const routes = { | ||
'/intro': document.getElementById('intro'), | ||
'/': document.getElementById('home'), | ||
'/options': document.getElementById('options') | ||
}; | ||
|
||
function navigate(path) { | ||
window.history.pushState({}, path, window.location.origin + path); | ||
updatePage(path); | ||
} | ||
|
||
function updatePage(path) { | ||
Object.values(routes).forEach(page => page.classList.remove('active')); | ||
if (routes[path]) { | ||
routes[path].classList.add('active'); | ||
} | ||
} | ||
|
||
window.onpopstate = () => updatePage(window.location.pathname); | ||
|
||
// Initial load | ||
updatePage(window.location.pathname || '/'); | ||
|
||
// Example navigation buttons (you can remove these if not needed) | ||
document.body.insertAdjacentHTML('beforeend', ` | ||
<nav> | ||
<button onclick="navigate('/intro')">Signin page</button> | ||
<button onclick="navigate('/')">Home page</button> | ||
<button onclick="navigate('/options')">Export page</button> | ||
</nav> | ||
`); | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.