Verifying the password flow needs a login to actually happen, and driving one by hand means a human at the keyboard. ?auto=1 fills the fields and logs in on load, with user, pass, succeed, rmdom and push as query parameters so each trigger and the failure case can be exercised without touching a real account. Values are written with the native setter followed by a real 'input' event, which is what a keystroke produces and what inject.js listens for. Worth being clear about the limit: this proves the trigger fires for a field that reached its value the way a browser reports it, not that physical typing works.
139 lines
5.1 KiB
HTML
139 lines
5.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Login Test</title>
|
|
<style>
|
|
body { font-family: -apple-system, sans-serif; max-width: 380px;
|
|
margin: 80px auto; padding: 0 20px; }
|
|
input, button { display: block; width: 100%; margin: 8px 0;
|
|
padding: 10px; box-sizing: border-box; font-size: 14px; }
|
|
button { cursor: pointer; background: #0ea5e9; color: white;
|
|
border: none; border-radius: 6px; }
|
|
button.sec { background: #64748b; }
|
|
.err { color: #dc2626; margin: 8px 0; font-size: 13px; }
|
|
.ok { color: #16a34a; margin: 8px 0; font-size: 13px; }
|
|
label { display: flex; align-items: center; gap: 8px;
|
|
font-size: 13px; margin: 4px 0; }
|
|
fieldset { border: 1px solid #e2e8f0; border-radius: 8px;
|
|
padding: 12px; margin-bottom: 16px; }
|
|
legend { font-size: 12px; font-weight: 600; color: #475569; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h2>Work App — login trigger test</h2>
|
|
<p style="font-size:13px;color:#64748b">
|
|
Each scenario toggles which triggers fire. Watch for the
|
|
"Save this password?" dialog from Work.
|
|
</p>
|
|
|
|
<fieldset>
|
|
<legend>Scenario controls</legend>
|
|
<label><input type="checkbox" id="succeed" checked>
|
|
Simulate successful login</label>
|
|
<label><input type="checkbox" id="removeDom" checked>
|
|
Remove form from DOM on success (Trigger A)</label>
|
|
<label><input type="checkbox" id="pushUrl">
|
|
Change URL via pushState on success (Trigger B)</label>
|
|
</fieldset>
|
|
|
|
<div id="loginSection">
|
|
<form id="loginForm" onsubmit="return false">
|
|
<input type="email" id="email" placeholder="Email"
|
|
autocomplete="username">
|
|
<input type="password" id="password" placeholder="Password"
|
|
autocomplete="current-password">
|
|
<button type="button" onclick="doXhr()">
|
|
Login via XHR (no submit event) — tests Triggers A & B
|
|
</button>
|
|
<button type="submit" class="sec" onclick="doFormSubmit()">
|
|
Login via form submit — tests Trigger C
|
|
</button>
|
|
</form>
|
|
<div id="msg"></div>
|
|
</div>
|
|
|
|
<div id="dashboard" style="display:none">
|
|
<div class="ok">✓ Logged in — form removed from DOM.</div>
|
|
<div class="ok" id="urlNote"></div>
|
|
<button onclick="location.reload()">Reload to reset</button>
|
|
</div>
|
|
|
|
<script>
|
|
function doXhr() {
|
|
var succeed = document.getElementById('succeed').checked;
|
|
var rmDom = document.getElementById('removeDom').checked;
|
|
var pushUrl = document.getElementById('pushUrl').checked;
|
|
var msg = document.getElementById('msg');
|
|
|
|
msg.className = '';
|
|
msg.textContent = '';
|
|
|
|
if (!succeed) {
|
|
// Failed login: keep form, show error — offer must NOT fire
|
|
msg.className = 'err';
|
|
msg.textContent = 'Invalid credentials. Try again.';
|
|
return;
|
|
}
|
|
|
|
// Successful login
|
|
if (rmDom) {
|
|
document.getElementById('loginSection').remove();
|
|
document.getElementById('dashboard').style.display = '';
|
|
}
|
|
if (pushUrl) {
|
|
history.pushState({}, '', '/dashboard');
|
|
var n = document.getElementById('urlNote');
|
|
if (n) n.textContent = '✓ URL changed to /dashboard via pushState.';
|
|
}
|
|
}
|
|
|
|
function doFormSubmit() {
|
|
// inject.js's submit listener fires in capture phase before this.
|
|
// Default is already prevented by onsubmit="return false" on the form.
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
// Automation hook, so the page can drive itself when there is no human at
|
|
// the keyboard: ?auto=1&user=…&pass=…&succeed=1&rmdom=1&push=0
|
|
//
|
|
// Values are written with the native setter and followed by a real 'input'
|
|
// event, which is what a keystroke produces and what inject.js listens for.
|
|
// It does NOT prove that physical typing works — only that the trigger
|
|
// fires for a field that reached its value the way a browser reports it.
|
|
(function () {
|
|
var q = new URLSearchParams(location.search);
|
|
if (q.get('auto') !== '1') return;
|
|
|
|
function setNative(el, value) {
|
|
var proto = Object.getPrototypeOf(el);
|
|
var desc = Object.getOwnPropertyDescriptor(proto, 'value');
|
|
desc.set.call(el, value);
|
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
}
|
|
|
|
function check(id, on) {
|
|
var el = document.getElementById(id);
|
|
if (el && el.checked !== on) el.click();
|
|
}
|
|
|
|
window.addEventListener('load', function () {
|
|
check('succeed', q.get('succeed') !== '0');
|
|
check('removeDom', q.get('rmdom') !== '0');
|
|
check('pushUrl', q.get('push') === '1');
|
|
|
|
var email = document.getElementById('email');
|
|
var pw = document.getElementById('password');
|
|
if (email) { email.focus(); setNative(email, q.get('user') || 'testuser@example.com'); }
|
|
if (pw) { pw.focus(); setNative(pw, q.get('pass') || 'TestPassword123'); }
|
|
|
|
// Let the fields settle, then log in the way Google does: no submit event.
|
|
setTimeout(doXhr, 800);
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|