diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 118e4ae..6b61d51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,20 @@ jobs: with: node-version: "20" + - name: Install pinned Google Chrome + run: | + set -euo pipefail + sudo apt-get update + CHROME_VERSION="147.0.7727.137-1" + CHROME_DEB="google-chrome-stable_${CHROME_VERSION}_amd64.deb" + curl -fsSLo "$CHROME_DEB" "https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/${CHROME_DEB}" + sudo apt install -y --allow-downgrades "./${CHROME_DEB}" + google-chrome --version + - name: Install dependencies run: | set -euo pipefail - npm install + npm ci - name: Run checks run: | @@ -33,4 +43,6 @@ jobs: node tests/test_auth_code_extraction.js node tests/test_auth_retry.js node tests/test_proxy_config.js + node tests/test_totp_dependency.js + node tests/test_playwright_dependency.js bash tests/test_workflow_config_sources.sh diff --git a/tests/test_playwright_dependency.js b/tests/test_playwright_dependency.js new file mode 100644 index 0000000..42df30f --- /dev/null +++ b/tests/test_playwright_dependency.js @@ -0,0 +1,40 @@ +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { chromium } = require('playwright-extra'); +const stealth = require('puppeteer-extra-plugin-stealth')(); + +chromium.use(stealth); + +(async () => { + const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'schwab-playwright-smoke-')); + const context = await chromium.launchPersistentContext(userDataDir, { + channel: 'chrome', + headless: true, + }); + try { + const page = await context.newPage(); + await page.setContent(` + + waiting + + `); + + await page.getByRole('button', { name: 'Continue' }).click(); + await assert.doesNotReject(() => page.getByText('continued').waitFor()); + assert.strictEqual(await page.locator('#status').textContent(), 'continued'); + } finally { + await context.close(); + fs.rmSync(userDataDir, { recursive: true, force: true }); + } + + console.log('playwright-extra Chrome channel smoke passed'); +})().catch(error => { + console.error(error); + process.exitCode = 1; +}); diff --git a/tests/test_totp_dependency.js b/tests/test_totp_dependency.js new file mode 100644 index 0000000..306bf35 --- /dev/null +++ b/tests/test_totp_dependency.js @@ -0,0 +1,35 @@ +const assert = require('assert'); +const { Secret, TOTP } = require('otpauth'); + +// RFC 6238 SHA-1 vector at Unix time 59 seconds. +const base32Secret = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'; +const expectedCode = '94287082'; + +assert.strictEqual( + new TOTP({ secret: base32Secret, algorithm: 'SHA1', digits: 8, period: 30 }) + .generate({ timestamp: 59000 }), + expectedCode, +); + +assert.strictEqual( + new TOTP({ + secret: ` ${base32Secret.toLowerCase().slice(0, 10)}\n${base32Secret.toLowerCase().slice(10)} ` + .replace(/\s/g, ''), + algorithm: 'SHA1', + digits: 8, + period: 30, + }).generate({ timestamp: 59000 }), + expectedCode, +); + +assert.strictEqual( + new TOTP({ + secret: Secret.fromHex('3132333435363738393031323334353637383930'), + algorithm: 'SHA1', + digits: 8, + period: 30, + }).generate({ timestamp: 59000 }), + expectedCode, +); + +console.log('otpauth RFC 6238 vectors passed');