171 lines
11 KiB
JavaScript
171 lines
11 KiB
JavaScript
import { test, expect } from '@playwright/test';
|
|
|
|
test('create → mine → mempool → inscription → block/transaction → reload/load', async ({page}) => {
|
|
const errors=[], remote=[];
|
|
page.on('pageerror',e=>errors.push(e.message));
|
|
page.on('request',r=>{ if(!r.url().startsWith('http://127.0.0.1:4178')) remote.push(r.url()); });
|
|
await page.goto('/');
|
|
await expect(page.getByRole('button',{name:'Mine a block'})).toBeDisabled();
|
|
await expect(page.locator('#mempool-list')).toContainText('Mempool empty');
|
|
await page.getByRole('button',{name:'Create test wallet'}).click();
|
|
await expect(page.locator('#address')).toHaveValue(/^bcrt1p/);
|
|
await page.getByRole('button',{name:'Mine a block'}).click();
|
|
await expect(page.locator('#balance')).toHaveText('50.00000000');
|
|
await expect(page.locator('#blocks .block-card')).toHaveCount(1);
|
|
await expect(page.locator('#blocks .block-card').first()).toContainText('HEIGHT 1');
|
|
const payload='<script>window.unwanted=true</script> Hello 🌱';
|
|
await page.getByLabel('What will you put on your chain?').fill(payload);
|
|
await page.getByRole('button',{name:'Review inscription'}).click();
|
|
await expect(page.locator('#review-text')).toHaveText(payload);
|
|
await expect(page.locator('#pending')).toHaveText('0');
|
|
await page.getByRole('button',{name:'Sign & inscribe'}).click();
|
|
await expect(page.locator('#pending')).toHaveText('2');
|
|
await expect(page.locator('#mempool-list .mempool-card')).toHaveCount(2);
|
|
await expect(page.locator('#mempool-list')).toContainText('INSCRIPTION COMMIT');
|
|
await expect(page.locator('#mempool-list')).toContainText('INSCRIPTION REVEAL');
|
|
await expect(page.locator('.collectible pre')).toHaveText(payload);
|
|
await expect(page.locator('.collectible .tag')).toContainText('PENDING');
|
|
await expect(page.locator('#balance')).toHaveText('49.99999000');
|
|
await page.locator('#mempool-list .mempool-card').last().click();
|
|
await expect(page.locator('#inspector')).toContainText('Pending in mempool');
|
|
await page.reload();
|
|
await expect(page.locator('.collectible pre')).toHaveText(payload);
|
|
await expect(page.locator('#pending')).toHaveText('2');
|
|
await expect(page.locator('#mempool-list .mempool-card')).toHaveCount(2);
|
|
await page.getByRole('button',{name:'Mine a block'}).click();
|
|
await expect(page.locator('#mempool-list')).toContainText('Mempool empty');
|
|
await expect(page.locator('#blocks .block-card')).toHaveCount(2);
|
|
await expect(page.locator('#blocks .block-card').first()).toContainText('HEIGHT 2');
|
|
await expect(page.locator('.collectible .tag')).toHaveText('CONFIRMED · BLOCK 2');
|
|
await page.getByRole('link',{name:'View reveal transaction'}).click();
|
|
await expect(page.locator('#inspector')).toContainText('1 confirmation(s)');
|
|
await expect(page.locator('#inspector')).toContainText('Raw Bitcoin transaction');
|
|
await expect(page.locator('#inspector')).toContainText('INSCRIPTION REVEAL');
|
|
await page.getByRole('link',{name:'Inspect commit transaction'}).click();
|
|
await expect(page.locator('#inspector')).toContainText('4,999,998,000 sats');
|
|
await expect(page.locator('#inspector')).toContainText('INSCRIPTION COMMIT');
|
|
await page.getByRole('link',{name:'View block 2'}).click();
|
|
await expect(page.locator('#inspector h2')).toHaveText('Block 2');
|
|
await expect(page.locator('#inspector')).toContainText('Transactions');
|
|
await expect(page.locator('#inspector a')).toHaveCount(4);
|
|
await page.getByRole('link',{name:/^Inscription /}).click();
|
|
await expect(page.locator('#inspector')).toContainText('Inscription detail');
|
|
const [download] = await Promise.all([page.waitForEvent('download'),page.getByRole('button',{name:'Save complete lab'}).click()]);
|
|
const backupPath=await download.path();
|
|
page.on('dialog',dialog=>dialog.accept());
|
|
await page.getByRole('button',{name:'Reset local lab'}).click();
|
|
await expect(page.getByRole('button',{name:'Create test wallet'})).toBeVisible();
|
|
await page.locator('#load-wallet').setInputFiles(backupPath);
|
|
await expect(page.locator('.collectible pre')).toHaveText(payload);
|
|
await expect(page.locator('#height')).toHaveText('2');
|
|
await page.reload();
|
|
await expect(page.locator('#balance')).toHaveText('99.99999000');
|
|
expect(await page.evaluate(()=>window.unwanted)).toBeUndefined();
|
|
expect(errors).toEqual([]); expect(remote).toEqual([]);
|
|
expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth)).toBeTruthy();
|
|
await page.screenshot({path:`test-results/${test.info().project.name}-complete.png`,fullPage:true});
|
|
});
|
|
|
|
test('theme toggle persists explicit light/dark preference', async ({page}) => {
|
|
await page.emulateMedia({colorScheme:'dark'});
|
|
await page.goto('/');
|
|
await expect(page.locator('html')).toHaveAttribute('data-theme','dark');
|
|
await expect(page.getByRole('button',{name:'Switch to light mode'})).toBeVisible();
|
|
await page.getByRole('button',{name:'Switch to light mode'}).click();
|
|
await expect(page.locator('html')).toHaveAttribute('data-theme','light');
|
|
await page.reload();
|
|
await expect(page.locator('html')).toHaveAttribute('data-theme','light');
|
|
await expect(page.getByRole('button',{name:'Switch to dark mode'})).toBeVisible();
|
|
});
|
|
|
|
test('wallet backup reload and rejected oversized or invalid input',async({page})=>{
|
|
await page.goto('/'); await page.getByRole('button',{name:'Create test wallet'}).click();
|
|
await expect(page.locator('#address')).toHaveValue(/^bcrt1p/);
|
|
const address=await page.locator('#address').inputValue();
|
|
const [download]=await Promise.all([page.waitForEvent('download'),page.getByRole('button',{name:'Save test wallet'}).click()]);
|
|
await page.getByRole('button',{name:'Mine a block'}).click();
|
|
await page.locator('#payload').fill('🌱'.repeat(4097));
|
|
await page.getByRole('button',{name:'Review inscription'}).click();
|
|
await expect(page.getByRole('status')).toContainText('at most 16384');
|
|
await expect(page.locator('#pending')).toHaveText('0');
|
|
await page.locator('#load-wallet').setInputFiles({name:'bad.json',mimeType:'application/json',buffer:Buffer.from('{"network":"mainnet"}')});
|
|
await expect(page.getByRole('status')).toContainText('Only Super Testnet');
|
|
await expect(page.locator('#address')).toHaveValue(address);
|
|
page.on('dialog',d=>d.accept());
|
|
await page.locator('#load-wallet').setInputFiles(await download.path());
|
|
await expect(page.getByRole('status')).toContainText('Test wallet loaded.');
|
|
await expect(page.locator('#address')).toHaveValue(address);
|
|
await expect(page.locator('#balance')).toHaveText('50.00000000');
|
|
});
|
|
|
|
test('two tabs serialize mining and refresh shared storage',async({page,context})=>{
|
|
await page.goto('/'); await page.getByRole('button',{name:'Create test wallet'}).click();
|
|
await expect(page.locator('#address')).toHaveValue(/^bcrt1p/);
|
|
const other=await context.newPage(); await other.goto('/');
|
|
await Promise.all([page.getByRole('button',{name:'Mine a block'}).click(),other.getByRole('button',{name:'Mine a block'}).click()]);
|
|
await expect(page.locator('#height')).toHaveText('2');
|
|
await expect(other.locator('#height')).toHaveText('2');
|
|
await expect(page.locator('#balance')).toHaveText('100.00000000');
|
|
});
|
|
|
|
test('storage failure leaves the displayed and persisted chain unchanged', async ({page}) => {
|
|
await page.goto('/');
|
|
await page.getByRole('button',{name:'Create test wallet'}).click();
|
|
await expect(page.locator('#address')).toHaveValue(/^bcrt1p/);
|
|
await page.evaluate(()=>{ Storage.prototype.setItem = () => { throw new DOMException('Quota exceeded','QuotaExceededError'); }; });
|
|
await page.getByRole('button',{name:'Mine a block'}).click();
|
|
await expect(page.getByRole('status')).toContainText('No changes were saved');
|
|
await expect(page.locator('#height')).toHaveText('0');
|
|
await expect(page.locator('#balance')).toHaveText('0.00000000');
|
|
await page.reload();
|
|
await expect(page.locator('#height')).toHaveText('0');
|
|
await page.getByRole('button',{name:'Mine a block'}).click();
|
|
await expect(page.locator('#balance')).toHaveText('50.00000000');
|
|
});
|
|
test('MIME upload, reset only after success, and parent/child navigation', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.locator('#create-wallet').click(); await page.locator('#mine').click();
|
|
await page.locator('#payload').fill('parent'); await page.locator('#review').click(); await page.locator('#sign').click();
|
|
await expect(page.locator('#payload')).toHaveValue('');
|
|
await expect(page.locator('#byte-count')).toHaveText('0 / 16384 bytes');
|
|
const parent = await page.locator('#parent option').nth(1).getAttribute('value');
|
|
await page.locator('#parent').selectOption(parent);
|
|
await page.locator('#body-file').setInputFiles({ name: 'art.svg', mimeType: 'image/svg+xml', buffer: Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8" fill="orange"/></svg>') });
|
|
await page.locator('#review').click();
|
|
await expect(page.locator('#review-preview iframe')).toHaveAttribute('sandbox', '');
|
|
await page.evaluate(() => { window.originalSetItem = Storage.prototype.setItem; Storage.prototype.setItem = () => { throw new Error('quota'); }; });
|
|
await page.locator('#sign').click();
|
|
await expect(page.getByRole('status')).toContainText('No changes were saved');
|
|
await expect(page.locator('#review-panel')).toBeVisible();
|
|
await expect(page.locator('#parent')).toHaveValue(parent);
|
|
expect(await page.locator('#body-file').evaluate(e => e.files.length)).toBe(1);
|
|
await page.evaluate(() => { Storage.prototype.setItem = window.originalSetItem; });
|
|
await page.locator('#sign').click();
|
|
await expect(page.locator('.collectible')).toHaveCount(2);
|
|
await expect(page.locator('#review-panel')).toBeHidden();
|
|
await expect(page.locator('#parent')).toHaveValue('');
|
|
await expect(page.locator('#byte-count')).toHaveText('0 / 16384 bytes');
|
|
expect(await page.locator('#body-file').evaluate(e => e.files.length)).toBe(0);
|
|
await page.locator('.collectible').getByRole('link', { name: /^Parent / }).click();
|
|
await expect(page.locator('#inspector pre').first()).toHaveText('parent');
|
|
await page.locator('#inspector').getByRole('link', { name: /^Child / }).click();
|
|
await expect(page.locator('#inspector')).toContainText('image/svg+xml');
|
|
await page.reload();
|
|
await expect(page.locator('#inspector iframe')).toBeVisible();
|
|
});
|
|
|
|
test('HTML preview cannot execute, navigate or fetch remote resources', async ({ page }) => {
|
|
const remote = [];
|
|
page.on('request', r => { if (/^https?:/.test(r.url()) && !r.url().startsWith('http://127.0.0.1:4178')) remote.push(r.url()); });
|
|
await page.goto('/'); await page.locator('#create-wallet').click(); await page.locator('#mine').click();
|
|
await page.locator('#mime-type').selectOption('text/html');
|
|
await page.locator('#payload').fill('<meta http-equiv="refresh" content="0;url=https://example.com"><script>parent.unwanted=true</script><img src="https://example.com/a"><iframe src="https://example.com"></iframe><p style="background:url(https://example.com/b)" onclick="parent.unwanted=true">safe words</p><a href="https://example.com">link</a>');
|
|
await page.locator('#review').click();
|
|
await expect(page.frameLocator('#review-preview iframe').locator('p')).toHaveText('safe words');
|
|
await page.locator('#sign').click();
|
|
await expect(page.frameLocator('.collectible iframe').locator('p')).toHaveText('safe words');
|
|
expect(await page.evaluate(() => window.unwanted)).toBeUndefined();
|
|
expect(remote).toEqual([]);
|
|
await expect(page).toHaveURL('http://127.0.0.1:4178/');
|
|
});
|