mirror of
https://github.com/vernu/textbee.git
synced 2026-07-31 01:17:52 -04:00
Dashboard: - Removed the recent activity section. - Corrected the usage card wording. The number was labelled "sent", but getCurrentSubscription counts SMS documents with no type filter, and the same model stores both SMSType.SENT and SMSType.RECEIVED. Inbound messages consume the quota too, so the cards now say "messages" and carry an explicit note that sends and receipts both count against the limit. - "This month" is now "Last 30 days". The backend counts from setMonth(-1), a rolling window, not the calendar month. Devices: - Removed the battery and signal indicators. Neither the device schema nor the Android app reports either value, so they only ever rendered "unknown" and "-" beside an icon that implied real telemetry. Also dropped the phantom batteryLevel field from the Device type so nothing is built on it later. API guide, rebuilt: - It rendered a Collapsible that defaulted to closed. Once it moved to its own route the page looked empty until you found the chevron. It is a page now, so nothing is collapsed. - Samples are generated from the user's real device id, with a device picker, so they run after pasting one key. Every sample previously said YOUR_DEVICE_ID, so nothing on the page was runnable as-is. - Code blocks follow the theme. The old ones hardcoded a slate-800 background, so they were the only dark element on a light page. - Languages went from 3 to 5 (cURL, Node, Python, PHP, Go), picked once and applied to every endpoint, replacing a hand-rolled inline Python SVG icon. - Endpoints now cover bulk send and received messages, not just send and two lookups. Response examples use the real lowercase status values from the SMS schema instead of invented uppercase ones, and the batch example is no longer invalid JSON (it had a trailing comma). - Keys the copy state per block rather than by index arithmetic over Object.entries order. The mount guard usually needed for next-themes is not used in CodeBlock: resolvedTheme is undefined on the server and first client render, so both produce the light style. Verified with a console sweep across nine pages in both colour schemes, zero messages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
87 lines
3.0 KiB
TypeScript
87 lines
3.0 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
|
import { authenticate } from './session'
|
|
import { mockApi } from './mock-api'
|
|
import { mockDevices } from '../test/fixtures'
|
|
|
|
test.describe('api guide (mocked API, no real backend)', () => {
|
|
test('shows content immediately, not a collapsed accordion', async ({
|
|
page,
|
|
context,
|
|
}) => {
|
|
await authenticate(context)
|
|
await mockApi(page)
|
|
await page.goto('/dashboard/messaging/api-guide')
|
|
|
|
// The old guide rendered a collapsed panel that defaulted to closed, so
|
|
// this dedicated page looked empty until you found the chevron.
|
|
await expect(
|
|
page.getByRole('heading', { name: 'Send an SMS' })
|
|
).toBeVisible()
|
|
await expect(
|
|
page.getByRole('heading', { name: 'Send messages in bulk' })
|
|
).toBeVisible()
|
|
await expect(
|
|
page.getByRole('heading', { name: 'Read received messages' })
|
|
).toBeVisible()
|
|
})
|
|
|
|
test('samples use the real device id so they are runnable', async ({
|
|
page,
|
|
context,
|
|
}) => {
|
|
await authenticate(context)
|
|
await mockApi(page)
|
|
await page.goto('/dashboard/messaging/api-guide')
|
|
|
|
// Fixtures: first device id. The old guide always printed YOUR_DEVICE_ID.
|
|
const deviceId = mockDevices[0]._id
|
|
await expect(page.getByText(deviceId, { exact: false }).first()).toBeVisible()
|
|
await expect(page.getByText('YOUR_DEVICE_ID')).toHaveCount(0)
|
|
})
|
|
|
|
test('switching language swaps every sample', async ({ page, context }) => {
|
|
await authenticate(context)
|
|
await mockApi(page)
|
|
await page.goto('/dashboard/messaging/api-guide')
|
|
|
|
// cURL is the default.
|
|
await expect(page.getByText('curl -X POST').first()).toBeVisible()
|
|
|
|
await page.getByRole('tab', { name: 'Python' }).click()
|
|
await expect(page.getByText('import os, requests').first()).toBeVisible()
|
|
await expect(page.getByText('curl -X POST')).toHaveCount(0)
|
|
|
|
await page.getByRole('tab', { name: 'Go' }).click()
|
|
await expect(page.getByText('package main').first()).toBeVisible()
|
|
})
|
|
|
|
test('code can be copied', async ({ page, context }) => {
|
|
await authenticate(context)
|
|
await mockApi(page)
|
|
await context.grantPermissions(['clipboard-read', 'clipboard-write'])
|
|
await page.goto('/dashboard/messaging/api-guide')
|
|
|
|
await page.getByRole('button', { name: 'Copy code' }).first().click()
|
|
|
|
const clipboard = await page.evaluate(() =>
|
|
navigator.clipboard.readText()
|
|
)
|
|
expect(clipboard).toContain('api.textbee.dev')
|
|
})
|
|
|
|
test('does not scroll sideways at 375px', async ({ page, context }) => {
|
|
await page.setViewportSize({ width: 375, height: 800 })
|
|
await authenticate(context)
|
|
await mockApi(page)
|
|
await page.goto('/dashboard/messaging/api-guide')
|
|
await page.waitForLoadState('networkidle')
|
|
|
|
// Code blocks are the classic way to widen a mobile page.
|
|
const { scrollWidth, innerWidth } = await page.evaluate(() => ({
|
|
scrollWidth: document.documentElement.scrollWidth,
|
|
innerWidth: window.innerWidth,
|
|
}))
|
|
expect(scrollWidth).toBeLessThanOrEqual(innerWidth)
|
|
})
|
|
})
|