2023-11-23 01:04:20 +00:00
|
|
|
import { expect, test } from '@playwright/test';
|
|
|
|
|
2023-11-24 23:42:37 +00:00
|
|
|
const statusJson = {
|
|
|
|
currentScreen: 0,
|
|
|
|
numScreens: 7,
|
|
|
|
timerRunning: true,
|
|
|
|
espUptime: 4479,
|
|
|
|
espFreeHeap: 58508,
|
|
|
|
espHeapSize: 342108,
|
|
|
|
connectionStatus: { price: true, blocks: true },
|
|
|
|
rssi: -66,
|
|
|
|
data: ['BLOCK/HEIGHT', '8', '1', '8', '0', '2', '6'],
|
|
|
|
rendered: ['BLOCK/HEIGHT', '8', '1', '8', '0', '2', '6'],
|
|
|
|
leds: [
|
|
|
|
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
|
|
|
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
|
|
|
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
|
|
|
{ red: 0, green: 0, blue: 0, hex: '#000000' }
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
2023-11-26 01:01:50 +00:00
|
|
|
const settingsJson = {
|
|
|
|
numScreens: 7,
|
|
|
|
fgColor: 415029,
|
|
|
|
bgColor: 0,
|
|
|
|
timerSeconds: 1800,
|
|
|
|
timerRunning: true,
|
|
|
|
minSecPriceUpd: 30,
|
|
|
|
fullRefreshMin: 60,
|
|
|
|
wpTimeout: 600,
|
|
|
|
tzOffset: 0,
|
|
|
|
useBitcoinNode: false,
|
|
|
|
mempoolInstance: 'mempool.space',
|
|
|
|
ledTestOnPower: true,
|
|
|
|
ledFlashOnUpd: true,
|
|
|
|
ledBrightness: 128,
|
|
|
|
stealFocus: true,
|
|
|
|
mcapBigChar: true,
|
|
|
|
mdnsEnabled: true,
|
|
|
|
otaEnabled: true,
|
|
|
|
fetchEurPrice: false,
|
|
|
|
hostnamePrefix: 'btclock',
|
|
|
|
hostname: 'btclock-d60b14',
|
|
|
|
ip: '192.168.20.231',
|
|
|
|
txPower: 78,
|
|
|
|
gitRev: '25d8b92bcbc8938417c140355ea3ba99ff9eb4b7',
|
2024-08-31 17:54:43 +00:00
|
|
|
gitTag: '3.1.9',
|
|
|
|
bitaxeEnabled: false,
|
|
|
|
bitaxeHostname: 'bitaxe1',
|
|
|
|
nostrZapNotify: true,
|
2024-11-30 15:23:52 +00:00
|
|
|
mqttEnabled: false,
|
2024-08-31 17:54:43 +00:00
|
|
|
hwRev: 'REV_A_EPD_2_13',
|
|
|
|
fsRev: '4c5d9616212b27e3f05c35370f0befcf2c5a04b2',
|
|
|
|
nostrZapPubkey: 'b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422',
|
2023-11-26 01:01:50 +00:00
|
|
|
lastBuildTime: '1700666677',
|
|
|
|
screens: [
|
|
|
|
{ id: 0, name: 'Block Height', enabled: true },
|
|
|
|
{ id: 1, name: 'Sats per dollar', enabled: true },
|
|
|
|
{ id: 2, name: 'Ticker', enabled: true },
|
|
|
|
{ id: 3, name: 'Time', enabled: true },
|
|
|
|
{ id: 4, name: 'Halving countdown', enabled: true },
|
|
|
|
{ id: 5, name: 'Market Cap', enabled: true }
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
2023-11-23 01:35:09 +00:00
|
|
|
test.beforeEach(async ({ page }) => {
|
|
|
|
await page.route('*/**/api/status', async (route) => {
|
2023-11-24 23:42:37 +00:00
|
|
|
await route.fulfill({ json: statusJson });
|
2023-11-23 01:35:09 +00:00
|
|
|
});
|
2023-11-26 01:01:50 +00:00
|
|
|
|
|
|
|
await page.route('*/**/api/show/screen/1', async (route) => {
|
|
|
|
//if (route.request().url().includes('*/**/api/show/screen/1')) {
|
|
|
|
statusJson.currentScreen = 1;
|
|
|
|
statusJson.data = ['MSCW/TIME', ' ', ' ', '2', '6', '4', '4'];
|
|
|
|
statusJson.rendered = statusJson.data;
|
|
|
|
//}
|
|
|
|
|
|
|
|
await route.fulfill({ json: statusJson });
|
|
|
|
});
|
|
|
|
|
|
|
|
await page.route('*/**/api/show/screen/2', async (route) => {
|
|
|
|
statusJson.currentScreen = 2;
|
2024-09-03 09:37:34 +00:00
|
|
|
statusJson.data = ['BTC/USD', '$', '3', '7', '8', '2', '4'];
|
|
|
|
statusJson.rendered = statusJson.data;
|
2023-11-26 01:01:50 +00:00
|
|
|
|
|
|
|
await route.fulfill({ json: statusJson });
|
|
|
|
});
|
|
|
|
|
|
|
|
await page.route('*/**/api/show/screen/4', async (route) => {
|
|
|
|
statusJson.currentScreen = 4;
|
2024-09-03 09:37:34 +00:00
|
|
|
statusJson.data = ['BIT/COIN', 'HALV/ING', '0/YRS', '149/DAYS', '8/HRS', '30/MINS', 'TO/GO'];
|
|
|
|
statusJson.rendered = statusJson.data;
|
2023-11-26 01:01:50 +00:00
|
|
|
|
|
|
|
await route.fulfill({ json: statusJson });
|
|
|
|
});
|
|
|
|
|
2023-11-23 01:35:09 +00:00
|
|
|
await page.route('*/**/api/settings', async (route) => {
|
2023-11-26 01:01:50 +00:00
|
|
|
await route.fulfill({ json: settingsJson });
|
2023-11-23 01:35:09 +00:00
|
|
|
});
|
|
|
|
|
2023-11-24 23:42:37 +00:00
|
|
|
await page.route('**/events', (route) => {
|
2023-11-26 01:01:50 +00:00
|
|
|
const newStatus = statusJson;
|
|
|
|
newStatus.data = ['BLOCK/HEIGHT', '8', '0', '0', '8', '1', '5'];
|
2023-11-24 23:42:37 +00:00
|
|
|
|
|
|
|
// Respond with a custom SSE message
|
|
|
|
route.fulfill({
|
|
|
|
status: 200,
|
|
|
|
contentType: 'text/event-stream',
|
2023-11-26 01:01:50 +00:00
|
|
|
json: `${JSON.stringify(newStatus)}\n\n`
|
2023-11-24 23:42:37 +00:00
|
|
|
});
|
|
|
|
});
|
2023-11-23 01:04:20 +00:00
|
|
|
});
|
|
|
|
|
2023-11-24 23:42:37 +00:00
|
|
|
test('index page has expected columns control, status, settings', async ({ page }) => {
|
2023-11-23 01:04:20 +00:00
|
|
|
await page.goto('/');
|
2023-11-24 23:42:37 +00:00
|
|
|
await expect(page.getByRole('heading', { name: 'Control' })).toBeVisible();
|
|
|
|
await expect(page.getByRole('heading', { name: 'Status' })).toBeVisible();
|
2023-11-23 01:04:20 +00:00
|
|
|
await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible();
|
|
|
|
});
|
|
|
|
|
2023-11-24 23:42:37 +00:00
|
|
|
test('index page has working language selector', async ({ page }) => {
|
2023-11-23 01:04:20 +00:00
|
|
|
await page.goto('/');
|
2023-11-24 23:42:37 +00:00
|
|
|
await expect(page.locator('//*[@id="nav-language-dropdown"]/a')).toBeVisible();
|
|
|
|
page.locator('//*[@id="nav-language-dropdown"]/a').click();
|
2024-09-03 10:11:14 +00:00
|
|
|
//*[@id="nav-language-dropdown"]/ul/li[1]/button
|
|
|
|
await expect(page.locator('//*[@id="nav-language-dropdown"]/ul/li[1]/button')).toBeVisible();
|
|
|
|
page.locator('//*[@id="nav-language-dropdown"]/ul/li[2]/button').click();
|
2023-11-24 23:42:37 +00:00
|
|
|
await expect(page.getByRole('heading', { name: 'Instellingen' })).toBeVisible();
|
|
|
|
page.locator('//*[@id="nav-language-dropdown"]/a').click();
|
2024-09-03 10:11:14 +00:00
|
|
|
page.locator('//*[@id="nav-language-dropdown"]/ul/li[3]/button').click();
|
2023-11-24 23:42:37 +00:00
|
|
|
await expect(page.getByRole('heading', { name: 'Configuración' })).toBeVisible();
|
2023-11-23 01:04:20 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
test('api page has expected load button', async ({ page }) => {
|
|
|
|
await page.goto('/api');
|
|
|
|
await expect(page.getByRole('button', { name: 'Load' })).toBeVisible();
|
|
|
|
});
|
2023-11-24 23:42:37 +00:00
|
|
|
|
|
|
|
test('timezone can be negative, zero and positive', async ({ page }) => {
|
|
|
|
await page.goto('/');
|
2024-11-28 16:49:38 +00:00
|
|
|
await page.getByRole('button', { name: 'Show all' }).click();
|
|
|
|
|
2023-11-24 23:42:37 +00:00
|
|
|
const tzOffsetField = 'input#tzOffset';
|
|
|
|
|
|
|
|
for (const val of ['-10', '0', '42']) {
|
|
|
|
await page.fill(tzOffsetField, val);
|
|
|
|
const resultValue = await page.$eval(tzOffsetField, (input: HTMLInputElement) => input.value);
|
|
|
|
expect(resultValue).toBe(val);
|
|
|
|
await page.getByRole('button', { name: 'Save' }).click();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
test('time values can not be zero or negative', async ({ page }) => {
|
|
|
|
await page.goto('/');
|
2024-11-28 16:49:38 +00:00
|
|
|
await page.getByRole('button', { name: 'Show all' }).click();
|
2023-11-24 23:42:37 +00:00
|
|
|
|
|
|
|
for (const field of ['#timePerScreen', '#fullRefreshMin', '#minSecPriceUpd']) {
|
|
|
|
for (const val of ['42', '210']) {
|
|
|
|
await page.fill(field, val);
|
|
|
|
const resultValue = await page.$eval(field, (input: HTMLInputElement) => input.value);
|
|
|
|
expect(resultValue).toBe(val);
|
|
|
|
await page.getByRole('button', { name: 'Save' }).click();
|
|
|
|
const validationMessage = await page.$eval(
|
|
|
|
field,
|
|
|
|
(input: HTMLInputElement) => input.validationMessage
|
|
|
|
);
|
|
|
|
expect(validationMessage).not.toContain('Value must be greater');
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const val of ['-10', '0']) {
|
|
|
|
await page.fill(field, val);
|
|
|
|
const resultValue = await page.$eval(field, (input: HTMLInputElement) => input.value);
|
|
|
|
expect(resultValue).toBe(val);
|
|
|
|
await page.getByRole('button', { name: 'Save' }).click();
|
|
|
|
const validationMessage = await page.$eval(
|
|
|
|
field,
|
|
|
|
(input: HTMLInputElement) => input.validationMessage
|
|
|
|
);
|
|
|
|
expect(validationMessage).toContain('Value must be greater');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
test('info message when fetch eur price is enabled', async ({ page }) => {
|
|
|
|
await page.goto('/');
|
2024-11-28 16:49:38 +00:00
|
|
|
await page.getByRole('button', { name: 'Show all' }).click();
|
|
|
|
|
2023-11-24 23:42:37 +00:00
|
|
|
const inputField = 'input#fetchEurPrice';
|
2024-09-03 09:20:07 +00:00
|
|
|
const switchElement = await page.locator(inputField);
|
2023-11-24 23:42:37 +00:00
|
|
|
|
|
|
|
expect(switchElement).toBeTruthy();
|
|
|
|
const isSwitchEnabled = await switchElement.isChecked();
|
|
|
|
expect(isSwitchEnabled).toBe(false);
|
|
|
|
|
|
|
|
await expect(page.getByText('the WS Price connection will show')).toBeHidden();
|
|
|
|
|
|
|
|
await switchElement.click();
|
|
|
|
const isSwitchNowEnabled = await switchElement.isChecked();
|
|
|
|
expect(isSwitchNowEnabled).toBe(true);
|
|
|
|
|
|
|
|
await expect(page.getByText('the WS Price connection will show')).toBeVisible();
|
|
|
|
});
|
|
|
|
|
2024-08-31 17:54:43 +00:00
|
|
|
test('npub values will be converted to hex pubkeys', async ({ page }) => {
|
|
|
|
await page.goto('/');
|
2024-11-28 16:49:38 +00:00
|
|
|
await page.getByRole('button', { name: 'Show all' }).click();
|
2024-08-31 17:54:43 +00:00
|
|
|
|
|
|
|
for (const field of ['#nostrZapPubkey']) {
|
|
|
|
for (const val of ['npub1k5f85zx0xdskyayqpfpc0zq6n7vwqjuuxugkayk72fgynp34cs3qfcvqg2']) {
|
|
|
|
await page.fill(field, val);
|
|
|
|
|
|
|
|
await page.getByLabel('Nostr Relay').click();
|
|
|
|
const resultValue = await page.$eval(field, (input: HTMLInputElement) => input.value);
|
|
|
|
|
|
|
|
expect(resultValue).toBe('b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
test('empty nostr relay field is not accepted', async ({ page }) => {
|
|
|
|
await page.goto('/');
|
2024-11-28 16:49:38 +00:00
|
|
|
await page.getByRole('button', { name: 'Show all' }).click();
|
2024-08-31 17:54:43 +00:00
|
|
|
|
|
|
|
const nostrRelayField = page.getByLabel('Nostr Relay');
|
|
|
|
|
|
|
|
nostrRelayField.fill('');
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: 'Save' }).click();
|
|
|
|
const validationMessage = await nostrRelayField.evaluate((el) => el.validationMessage);
|
|
|
|
|
|
|
|
expect(validationMessage).toContain('Please fill out this field');
|
|
|
|
});
|
|
|
|
|
2023-11-26 01:01:50 +00:00
|
|
|
test('screens should be able to change', async ({ page }) => {
|
|
|
|
await page.goto('/');
|
|
|
|
await expect(page.getByRole('button', { name: 'Sats per Dollar' })).toBeVisible();
|
|
|
|
const responsePromise = page.waitForRequest('*/**/api/show/screen/*');
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: 'Sats per Dollar' }).click();
|
|
|
|
const response = await responsePromise;
|
|
|
|
expect(response.url()).toContain('api/show/screen/1');
|
|
|
|
});
|
|
|
|
|
2023-11-24 23:42:37 +00:00
|
|
|
test('parse all types of EPD content correctly', async ({ page }) => {
|
|
|
|
statusJson.data[2] = '123';
|
|
|
|
|
2023-11-26 01:01:50 +00:00
|
|
|
await page.route('**/events', (route) => {
|
|
|
|
const newStatus = statusJson;
|
|
|
|
newStatus.data = ['BLOCK/HEIGHT', '8', '123', '0', '8', '1', '5'];
|
|
|
|
|
|
|
|
// Respond with a custom SSE message
|
|
|
|
route.fulfill({
|
|
|
|
status: 200,
|
|
|
|
contentType: 'text/event-stream',
|
|
|
|
json: `${JSON.stringify(newStatus)}\n\n`
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-11-24 23:42:37 +00:00
|
|
|
await page.goto('/');
|
|
|
|
|
|
|
|
await expect(page.getByRole('heading', { name: 'Status' })).toBeVisible();
|
|
|
|
await page.waitForSelector('#timerStatusText:has-text("running")');
|
|
|
|
await page.waitForSelector('#btclock-wrapper > div > div:nth-child(1)');
|
|
|
|
|
|
|
|
expect(statusJson.data[0]).toContain('/');
|
|
|
|
await expect(page.locator('#btclock-wrapper > div > div:nth-child(1)')).toBeTruthy();
|
|
|
|
await expect(page.locator('#btclock-wrapper > div > div:nth-child(1)')).toHaveClass('splitText');
|
|
|
|
expect(statusJson.data[1]).toHaveLength(1);
|
|
|
|
await expect(page.locator('#btclock-wrapper > div > div:nth-child(2)')).toHaveClass('digit');
|
|
|
|
expect(statusJson.data[2]).toHaveLength(3);
|
|
|
|
await expect(page.locator('#btclock-wrapper > div > div:nth-child(3)')).toHaveClass('mediumText');
|
|
|
|
});
|
2023-11-26 01:01:50 +00:00
|
|
|
|
|
|
|
test('should work with more than 7 screens', async ({ page }) => {
|
|
|
|
statusJson.data[2] = '1';
|
|
|
|
statusJson.numScreens = 9;
|
|
|
|
settingsJson.numScreens = 9;
|
|
|
|
statusJson.data.splice(1, 0, ' ', ' ');
|
|
|
|
|
|
|
|
await page.goto('/');
|
|
|
|
|
|
|
|
await expect(page.getByRole('heading', { name: 'Status' })).toBeVisible();
|
|
|
|
await page.waitForSelector('#timerStatusText:has-text("running")');
|
|
|
|
await expect(page.locator('#btclock-wrapper > div > div:nth-child(9)')).toBeTruthy();
|
|
|
|
|
|
|
|
await expect(page.locator('#customText')).toHaveAttribute(
|
|
|
|
'maxlength',
|
|
|
|
statusJson.numScreens.toString()
|
|
|
|
);
|
|
|
|
});
|