import { chromium } from 'playwright' import { createServer } from 'node:http' import { readFile, mkdir } from 'node:fs/promises' import { extname, resolve } from 'node:path' import assert from 'node:assert/strict' const root = resolve('dist') const server = createServer(async (request, response) => { try { const path = decodeURIComponent(new URL(request.url, 'http://localhost').pathname) const file = resolve(root, '.' + (path === '/' ? '/index.html' : path)) if (!file.startsWith(root + '\\') && !file.startsWith(root + '/')) throw new Error('outside assets') const data = await readFile(file) response.writeHead(200, { 'Content-Type': ({ '.html': 'text/html', '.mjs': 'text/javascript', '.js': 'text/javascript', '.css': 'text/css' })[extname(file)] || 'application/octet-stream' }); response.end(data) } catch { response.writeHead(404); response.end() } }) await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) const browser = await chromium.launch({ channel: 'msedge', headless: true }) try { const page = await browser.newPage({ viewport: { width: 390, height: 844 }, deviceScaleFactor: 1 }) const errors = []; page.on('pageerror', error => errors.push(error.message)) await page.goto(`http://127.0.0.1:${server.address().port}`) await page.getByRole('heading', { name: 'Fikrini konuş. Birlikte geliştir.' }).waitFor() assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false) assert.ok((await page.getByRole('button', { name: 'Eşleştirme kodu oluştur' }).boundingBox()).height >= 48) // UI wiring only: real account authentication is covered by gateway HTTP/DB // tests. Never submit a credential to the live portal from this smoke test. const loginCalls = [] await page.route('https://turkservis.online/v1/aiturk/mobile-auth/**', async route => { const action = new URL(route.request().url()).pathname.split('/').at(-1); loginCalls.push(action) const result = action === 'start' ? { deviceCode: 'tsml_fixture', userCode: 'ABCDEFGH', verificationUri: 'https://turkservis.online/account/aiturk/mobile-login/11111111-1111-4111-8111-111111111111' } : action === 'poll' ? { state: 'approved', accountToken: 'tsml_fixture' } : { revoked: true } await route.fulfill({ status: 200, contentType: 'application/json', headers: { 'Access-Control-Allow-Origin': '*' }, body: JSON.stringify(result) }) }) await page.getByRole('button', { name: 'AITURK hesabımla giriş yap' }).click() await page.getByText('Yalnız kendi hesabınızda bu kodu onaylayın: ABCDEFGH').waitFor() await page.getByRole('button', { name: 'Girişi kontrol et' }).click() await page.getByText('Hesaba giriş yapıldı.', { exact: true }).waitFor() await page.getByRole('button', { name: 'Bu uygulamadaki hesaptan çık' }).click() await page.getByText('Bu uygulamanın oturumu kapatıldı.', { exact: false }).waitFor() assert.deepEqual(loginCalls, ['start', 'poll', 'logout']) await mkdir('test-results', { recursive: true }) await page.screenshot({ path: 'test-results/android-onboarding.png', fullPage: true }) const result = await page.evaluate(async () => { const { completeIce, DataRequests, sendFrame } = await import('./transport.mjs') const phone = new RTCPeerConnection(), desktop = new RTCPeerConnection() try { const channel = phone.createDataChannel('aiturk-mobile-v1'), requests = new DataRequests(channel, () => {}) const remoteReady = new Promise(resolve => { desktop.ondatachannel = event => { event.channel.onmessage = incoming => { const request = JSON.parse(incoming.data) sendFrame(event.channel, { id: request.id, result: { action: request.action, text: 'Türkçe 🟢 '.repeat(24000) } }) } event.channel.onopen = resolve } }) await phone.setLocalDescription(await phone.createOffer()); await completeIce(phone) await desktop.setRemoteDescription(phone.localDescription) await desktop.setLocalDescription(await desktop.createAnswer()); await completeIce(desktop) await phone.setRemoteDescription(desktop.localDescription) await Promise.race([remoteReady, new Promise((_, reject) => setTimeout(() => reject(new Error('WebRTC timeout')), 15000))]) if (channel.readyState !== 'open') await new Promise(resolve => channel.onopen = resolve) const response = await requests.call('session.history', { sessionId: 'fixture' }) return { length: response.text.length, expected: 'Türkçe 🟢 '.repeat(24000).length, action: response.action, connectionState: phone.connectionState, dtls: phone.sctp.transport.state } } finally { phone.close(); desktop.close() } }) assert.equal(result.length, result.expected); assert.equal(result.dtls, 'connected'); assert.equal(result.action, 'session.history') assert.deepEqual(errors, []) console.log(JSON.stringify({ onboarding: 'passed', mockedLoginUi: 'passed', realWebRTC: result, errors })) } finally { await browser.close(); server.close() }