mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 02:01:40 +00:00
102af69e48
Complete the FlipTok UI, store, mock API, and server audit. Add reliable photo-slide publishing, comments, privacy checks, action error handling, deletion, localization, and profile interaction states. Replace the photo carousel scroll path with a GPU-accelerated drag and swipe track that supports rapid consecutive gestures, and cover the corrected behavior with focused contract, store, and mock smoke tests.
929 lines
26 KiB
JavaScript
929 lines
26 KiB
JavaScript
const assert = require('node:assert/strict')
|
|
const { once } = require('node:events')
|
|
|
|
const { app } = require('./index.cjs')
|
|
|
|
const browserDataRequests = [
|
|
['development:bootstrap', {}],
|
|
['account:devices', {}],
|
|
['banking:overview', {}],
|
|
['health:overview', {}],
|
|
['billing:overview', {}],
|
|
['billing:list', { filter: 'all', limit: 20, offset: 0 }],
|
|
['calendar:list', { endsAt: 4_102_444_800, startsAt: 0 }],
|
|
['calls:recents', {}],
|
|
['companies:list', {}],
|
|
['companies:my-requests', { limit: 20, offset: 0 }],
|
|
['companies:work-context', {}],
|
|
['companies:work-queue', { limit: 20, offset: 0 }],
|
|
['contacts:list', {}],
|
|
['crewlink:bootstrap', {}],
|
|
['crewlink:live', {}],
|
|
['crewlink:nearby', {}],
|
|
['darkchat:bootstrap', {}],
|
|
['easyshare:bootstrap', {}],
|
|
['easyshare:own-contact', {}],
|
|
['feather:bootstrap', {}],
|
|
['feather:feed', { limit: 20 }],
|
|
['feather:explore', { limit: 20 }],
|
|
['flare:bootstrap', {}],
|
|
['fliptok:bootstrap', {}],
|
|
['fliptok:feed', { limit: 20 }],
|
|
['fliptok:discover', { limit: 20 }],
|
|
['fliptok:activities', {}],
|
|
['gallery:list', {}],
|
|
['gallery:counts', {}],
|
|
['garage:vehicles', {}],
|
|
['garage:valet-state', {}],
|
|
['housing:overview', {}],
|
|
['housing:key-candidates', { action: 'give' }],
|
|
['mail:counts', {}],
|
|
['mail:list', { folder: 'inbox' }],
|
|
['mail:mailboxes', {}],
|
|
['map:getPlayerCoords', {}],
|
|
['map:markers', {}],
|
|
['marketplace:counts', {}],
|
|
['marketplace:list', {}],
|
|
['marketplace:list-own', {}],
|
|
['marketplace:list-inquiries', {}],
|
|
['marketplace:profile', {}],
|
|
['messages:conversations', {}],
|
|
['messages:gifs', { query: 'party' }],
|
|
['memos:list', {}],
|
|
['music:bootstrap', {}],
|
|
['notes:list', {}],
|
|
['pages:list', {}],
|
|
['pages:list-own', {}],
|
|
['pages:profile', {}],
|
|
['picstagram:bootstrap', {}],
|
|
['picstagram:feed', { limit: 20 }],
|
|
['picstagram:explore', { limit: 20 }],
|
|
['picstagram:saved', {}],
|
|
['picstagram:stories', {}],
|
|
['picstagram:activities', {}],
|
|
['radio:get', {}],
|
|
['skyride:bootstrap', {}],
|
|
['skyride:history', {}],
|
|
['skyride:get-player-coords', {}],
|
|
['weather:get', {}],
|
|
['weazel-news:context', {}],
|
|
['weazel-news:list', { category: null, offset: 0, search: '' }],
|
|
['weazel-news:manage-list', { offset: 0, search: '', status: 'all' }],
|
|
]
|
|
|
|
async function post(baseUrl, endpoint, body = {}) {
|
|
const response = await fetch(`${baseUrl}/api/${endpoint}`, {
|
|
body: JSON.stringify(body),
|
|
headers: { 'Content-Type': 'application/json' },
|
|
method: 'POST',
|
|
})
|
|
assert.equal(response.status, 200, endpoint)
|
|
return response.json()
|
|
}
|
|
|
|
async function expectSuccess(baseUrl, endpoint, body = {}, data = false) {
|
|
const result = await post(baseUrl, endpoint, body)
|
|
assert.equal(result.success, true, `${endpoint}: ${result.error ?? 'failed'}`)
|
|
if (data) assert.notEqual(result.data, undefined, `${endpoint}: missing data`)
|
|
return result.data
|
|
}
|
|
|
|
function expectItems(value, label, minimum = 1) {
|
|
assert(
|
|
Array.isArray(value) && value.length >= minimum,
|
|
`${label} did not include enough browser test data`,
|
|
)
|
|
}
|
|
|
|
function verifyBrowserTestData(dataByEndpoint) {
|
|
const development = dataByEndpoint.get('development:bootstrap')
|
|
expectItems(development.device.data.alarms.payload, 'clock alarms', 3)
|
|
expectItems(
|
|
development.device.data.media.payload.captures,
|
|
'camera captures',
|
|
2,
|
|
)
|
|
expectItems(development.memos, 'memos', 3)
|
|
expectItems(development.notes, 'notes', 4)
|
|
assert(
|
|
Object.keys(development.device.data.games.payload).length >= 7,
|
|
'games did not include saved browser test progress',
|
|
)
|
|
|
|
expectItems(dataByEndpoint.get('health:overview').days, 'health history', 7)
|
|
expectItems(
|
|
dataByEndpoint.get('billing:list').invoices,
|
|
'billing invoices',
|
|
3,
|
|
)
|
|
expectItems(dataByEndpoint.get('calendar:list'), 'calendar events', 5)
|
|
expectItems(dataByEndpoint.get('calls:recents'), 'recent calls', 5)
|
|
expectItems(dataByEndpoint.get('companies:list').companies, 'companies', 3)
|
|
expectItems(dataByEndpoint.get('contacts:list'), 'contacts', 10)
|
|
expectItems(
|
|
dataByEndpoint.get('crewlink:bootstrap').groups,
|
|
'CrewLink groups',
|
|
2,
|
|
)
|
|
expectItems(
|
|
dataByEndpoint.get('darkchat:bootstrap').conversations,
|
|
'DarkChat conversations',
|
|
)
|
|
expectItems(dataByEndpoint.get('feather:feed').items, 'Feather posts', 5)
|
|
expectItems(
|
|
dataByEndpoint.get('flare:bootstrap').suggestions,
|
|
'Flare suggestions',
|
|
5,
|
|
)
|
|
expectItems(dataByEndpoint.get('fliptok:feed').items, 'FlipTok videos', 2)
|
|
expectItems(dataByEndpoint.get('gallery:list'), 'gallery media', 30)
|
|
expectItems(
|
|
dataByEndpoint.get('garage:vehicles').vehicles,
|
|
'garage vehicles',
|
|
4,
|
|
)
|
|
expectItems(
|
|
dataByEndpoint.get('housing:overview').properties,
|
|
'housing properties',
|
|
5,
|
|
)
|
|
expectItems(dataByEndpoint.get('mail:list').items, 'inbox mail', 2)
|
|
expectItems(
|
|
dataByEndpoint.get('mail:mailboxes').mailboxes,
|
|
'custom mailboxes',
|
|
)
|
|
expectItems(dataByEndpoint.get('map:markers'), 'map markers')
|
|
expectItems(
|
|
dataByEndpoint.get('marketplace:list').items,
|
|
'CityMarkt listings',
|
|
4,
|
|
)
|
|
expectItems(
|
|
dataByEndpoint.get('messages:conversations'),
|
|
'message conversations',
|
|
5,
|
|
)
|
|
expectItems(
|
|
dataByEndpoint.get('music:bootstrap').serverTracks,
|
|
'music tracks',
|
|
3,
|
|
)
|
|
expectItems(dataByEndpoint.get('pages:list').items, 'Local Pages posts', 4)
|
|
expectItems(
|
|
dataByEndpoint.get('picstagram:feed').items,
|
|
'Picstagram posts',
|
|
2,
|
|
)
|
|
expectItems(dataByEndpoint.get('radio:get').history, 'radio history', 2)
|
|
expectItems(dataByEndpoint.get('skyride:history').items, 'SkyRide history', 2)
|
|
expectItems(
|
|
dataByEndpoint.get('weazel-news:list').items,
|
|
'Weazel News articles',
|
|
5,
|
|
)
|
|
expectItems(
|
|
dataByEndpoint.get('weazel-news:manage-list').items,
|
|
'Weazel News editorial articles',
|
|
7,
|
|
)
|
|
}
|
|
|
|
async function verifyStatefulActions(baseUrl) {
|
|
let gallery = await expectSuccess(baseUrl, 'gallery:list', {}, true)
|
|
assert(gallery.length >= 10, 'gallery:list did not include enough test media')
|
|
assert(
|
|
gallery.filter((item) => item.mediaType === 'video').length >= 3,
|
|
'gallery:list did not include enough test videos',
|
|
)
|
|
assert(
|
|
gallery
|
|
.filter((item) => item.mediaType === 'video')
|
|
.every((item) => typeof item.thumbnailUrl === 'string'),
|
|
'gallery:list returned a test video without a thumbnail',
|
|
)
|
|
const flipTokPhotos = gallery
|
|
.filter((item) => item.mediaType === 'photo')
|
|
.slice(0, 3)
|
|
const createdFlipTok = await expectSuccess(
|
|
baseUrl,
|
|
'fliptok:publish',
|
|
{
|
|
caption: 'Browser mock photo slideshow',
|
|
commentsEnabled: true,
|
|
coverTimeMs: 0,
|
|
customMusicUrl: '',
|
|
draft: false,
|
|
mediaId: flipTokPhotos[0].id,
|
|
mediaIds: flipTokPhotos.map((item) => item.id),
|
|
mediaType: 'photo',
|
|
musicTrack: '',
|
|
musicVolume: 0,
|
|
originalVolume: 0,
|
|
trimEndMs: null,
|
|
trimStartMs: 0,
|
|
visibility: 'public',
|
|
},
|
|
true,
|
|
)
|
|
let flipTokFeed = await expectSuccess(
|
|
baseUrl,
|
|
'fliptok:feed',
|
|
{ mode: 'for-you', offset: 0 },
|
|
true,
|
|
)
|
|
const photoFlipTok = flipTokFeed.items.find(
|
|
(item) => item.id === createdFlipTok.id,
|
|
)
|
|
assert.equal(photoFlipTok.media_type, 'photo')
|
|
assert.deepEqual(
|
|
photoFlipTok.media.map((item) => item.id),
|
|
flipTokPhotos.map((item) => item.id),
|
|
'fliptok:publish did not preserve photo order',
|
|
)
|
|
await expectSuccess(baseUrl, 'fliptok:comment', {
|
|
body: 'Browser mock scoped comment',
|
|
id: createdFlipTok.id,
|
|
})
|
|
const createdComments = await expectSuccess(
|
|
baseUrl,
|
|
'fliptok:comments',
|
|
{ id: createdFlipTok.id },
|
|
true,
|
|
)
|
|
assert(
|
|
createdComments.some((comment) => comment.body.includes('scoped comment')),
|
|
'fliptok:comment did not persist for its video',
|
|
)
|
|
const otherComments = await expectSuccess(
|
|
baseUrl,
|
|
'fliptok:comments',
|
|
{ id: 'fliptok-1' },
|
|
true,
|
|
)
|
|
assert(
|
|
!otherComments.some((comment) => comment.body.includes('scoped comment')),
|
|
'fliptok:comments leaked comments from another video',
|
|
)
|
|
await expectSuccess(baseUrl, 'fliptok:delete', { id: createdFlipTok.id })
|
|
flipTokFeed = await expectSuccess(
|
|
baseUrl,
|
|
'fliptok:feed',
|
|
{ mode: 'for-you', offset: 0 },
|
|
true,
|
|
)
|
|
assert(
|
|
!flipTokFeed.items.some((item) => item.id === createdFlipTok.id),
|
|
'fliptok:delete kept the removed video in the feed',
|
|
)
|
|
await expectSuccess(
|
|
baseUrl,
|
|
'gallery:favorite',
|
|
{ favorite: true, id: gallery[0].id },
|
|
true,
|
|
)
|
|
gallery = await expectSuccess(baseUrl, 'gallery:list', {}, true)
|
|
assert.equal(
|
|
gallery.find((item) => item.id === gallery[0].id)?.favorite,
|
|
true,
|
|
)
|
|
const articlePhotos = gallery
|
|
.filter((item) => item.mediaType === 'photo')
|
|
.slice(0, 7)
|
|
assert.equal(
|
|
articlePhotos.length,
|
|
7,
|
|
'gallery:list did not include enough photos for Weazel News',
|
|
)
|
|
const weazelContext = await expectSuccess(
|
|
baseUrl,
|
|
'weazel-news:context',
|
|
{},
|
|
true,
|
|
)
|
|
assert.equal(weazelContext.maximumImages, 6)
|
|
const createdArticleResponse = await expectSuccess(
|
|
baseUrl,
|
|
'weazel-news:create',
|
|
{
|
|
body: 'Created by the browser mock smoke test with several photos.',
|
|
category: 'news',
|
|
imageMediaIds: articlePhotos.slice(0, 3).map((item) => item.id),
|
|
status: 'published',
|
|
title: 'Browser test Weazel article',
|
|
},
|
|
true,
|
|
)
|
|
const createdArticle = createdArticleResponse.article
|
|
assert.deepEqual(
|
|
createdArticle.images.map((image) => image.mediaId),
|
|
articlePhotos.slice(0, 3).map((item) => item.id),
|
|
'weazel-news:create did not preserve image order',
|
|
)
|
|
assert.equal(createdArticle.imageMediaId, articlePhotos[0].id)
|
|
|
|
const updatedArticleResponse = await expectSuccess(
|
|
baseUrl,
|
|
'weazel-news:update',
|
|
{
|
|
body: createdArticle.body,
|
|
category: 'business',
|
|
id: createdArticle.id,
|
|
imageMediaIds: [articlePhotos[2].id, articlePhotos[0].id],
|
|
revision: createdArticle.revision,
|
|
status: 'draft',
|
|
title: 'Updated browser test Weazel article',
|
|
},
|
|
true,
|
|
)
|
|
const updatedArticle = updatedArticleResponse.article
|
|
assert.deepEqual(
|
|
updatedArticle.images.map((image) => image.mediaId),
|
|
[articlePhotos[2].id, articlePhotos[0].id],
|
|
'weazel-news:update did not preserve the reordered images',
|
|
)
|
|
assert.equal(updatedArticle.imageMediaId, articlePhotos[2].id)
|
|
|
|
const loadedArticleResponse = await expectSuccess(
|
|
baseUrl,
|
|
'weazel-news:get',
|
|
{ id: updatedArticle.id, manage: true },
|
|
true,
|
|
)
|
|
assert.deepEqual(loadedArticleResponse.article.images, updatedArticle.images)
|
|
|
|
const tooManyImages = await post(baseUrl, 'weazel-news:create', {
|
|
body: 'This article must be rejected because it has too many photos.',
|
|
category: 'news',
|
|
imageMediaIds: articlePhotos.map((item) => item.id),
|
|
status: 'draft',
|
|
title: 'Invalid Weazel article',
|
|
})
|
|
assert.equal(tooManyImages.success, false)
|
|
assert.equal(tooManyImages.error, 'invalid_attachment')
|
|
await expectSuccess(baseUrl, 'weazel-news:delete', {
|
|
id: updatedArticle.id,
|
|
revision: updatedArticle.revision,
|
|
})
|
|
|
|
const memoBootstrap = await expectSuccess(
|
|
baseUrl,
|
|
'development:bootstrap',
|
|
{},
|
|
true,
|
|
)
|
|
assert(
|
|
Array.isArray(memoBootstrap.memos) && memoBootstrap.memos.length >= 3,
|
|
'development:bootstrap did not include realistic memo data',
|
|
)
|
|
assert(
|
|
memoBootstrap.memos.every(
|
|
(memo) =>
|
|
typeof memo.url === 'string' &&
|
|
memo.url.length > 0 &&
|
|
Array.isArray(memo.waveform) &&
|
|
memo.waveform.length >= 8,
|
|
),
|
|
'development:bootstrap returned an invalid memo response shape',
|
|
)
|
|
|
|
let memos = await expectSuccess(baseUrl, 'memos:list', {}, true)
|
|
const capturedMemo = await expectSuccess(
|
|
baseUrl,
|
|
'memos:devCapture',
|
|
{
|
|
audioDataUrl: 'data:audio/webm;base64,T2dnUw==',
|
|
correlationId: 'browser-smoke-memo',
|
|
durationMs: 1_250,
|
|
mimeType: 'audio/webm;codecs=opus',
|
|
note: 'Recorded in the browser preview.',
|
|
pinned: false,
|
|
title: 'Browser recording',
|
|
waveform: Array(16).fill(0.35),
|
|
},
|
|
true,
|
|
)
|
|
assert.equal(capturedMemo.title, 'Browser recording')
|
|
assert.equal(capturedMemo.mimeType, 'audio/webm')
|
|
assert.equal(capturedMemo.sizeBytes, 4)
|
|
assert.match(capturedMemo.id, /^[0-9a-f-]{36}$/)
|
|
memos = await expectSuccess(baseUrl, 'memos:list', {}, true)
|
|
assert(
|
|
memos.some((item) => item.id === capturedMemo.id),
|
|
'memos:devCapture did not persist',
|
|
)
|
|
await expectSuccess(baseUrl, 'memos:delete', { id: capturedMemo.id }, true)
|
|
memos = await expectSuccess(baseUrl, 'memos:list', {}, true)
|
|
|
|
const memo = memos.find((item) => !item.pinned)
|
|
assert(memo, 'memos:list did not return an editable memo')
|
|
const updatedMemo = await expectSuccess(
|
|
baseUrl,
|
|
'memos:update',
|
|
{
|
|
id: memo.id,
|
|
note: 'Updated by the browser mock smoke test.',
|
|
pinned: true,
|
|
revision: memo.revision,
|
|
title: 'Updated browser memo',
|
|
},
|
|
true,
|
|
)
|
|
assert.equal(updatedMemo.id, memo.id)
|
|
assert.equal(updatedMemo.title, 'Updated browser memo')
|
|
assert.equal(updatedMemo.note, 'Updated by the browser mock smoke test.')
|
|
assert.equal(updatedMemo.pinned, true)
|
|
assert.equal(updatedMemo.revision, memo.revision + 1)
|
|
assert(Array.isArray(updatedMemo.waveform) && updatedMemo.waveform.length > 0)
|
|
|
|
const deletedMemo = await expectSuccess(
|
|
baseUrl,
|
|
'memos:delete',
|
|
{ id: updatedMemo.id },
|
|
true,
|
|
)
|
|
assert.deepEqual(deletedMemo, { id: updatedMemo.id })
|
|
memos = await expectSuccess(baseUrl, 'memos:list', {}, true)
|
|
assert(
|
|
!memos.some((item) => item.id === updatedMemo.id),
|
|
'memos:delete did not persist',
|
|
)
|
|
|
|
const noteId = `browser-note-${Date.now()}`
|
|
let notes = await expectSuccess(
|
|
baseUrl,
|
|
'notes:create',
|
|
{
|
|
body: 'Created by the browser mock smoke test.',
|
|
id: noteId,
|
|
title: 'Browser test',
|
|
},
|
|
true,
|
|
)
|
|
assert(
|
|
notes.some((note) => note.id === noteId),
|
|
'notes:create did not persist',
|
|
)
|
|
notes = await expectSuccess(
|
|
baseUrl,
|
|
'notes:update',
|
|
{
|
|
body: 'Updated browser test note.',
|
|
id: noteId,
|
|
title: 'Browser test updated',
|
|
},
|
|
true,
|
|
)
|
|
assert.equal(
|
|
notes.find((note) => note.id === noteId)?.title,
|
|
'Browser test updated',
|
|
)
|
|
notes = await expectSuccess(baseUrl, 'notes:delete', { id: noteId }, true)
|
|
assert(
|
|
!notes.some((note) => note.id === noteId),
|
|
'notes:delete did not persist',
|
|
)
|
|
|
|
const contact = await expectSuccess(
|
|
baseUrl,
|
|
'contacts:save',
|
|
{ name: 'Browser Tester', phoneNumber: '5552223333' },
|
|
true,
|
|
)
|
|
await expectSuccess(
|
|
baseUrl,
|
|
'contacts:favorite',
|
|
{ favorite: true, id: contact.id },
|
|
true,
|
|
)
|
|
let contacts = await expectSuccess(baseUrl, 'contacts:list', {}, true)
|
|
assert.equal(contacts.find((item) => item.id === contact.id)?.favorite, true)
|
|
await expectSuccess(baseUrl, 'contacts:delete', { id: contact.id })
|
|
contacts = await expectSuccess(baseUrl, 'contacts:list', {}, true)
|
|
assert(
|
|
!contacts.some((item) => item.id === contact.id),
|
|
'contacts:delete did not persist',
|
|
)
|
|
|
|
const event = await expectSuccess(
|
|
baseUrl,
|
|
'calendar:create',
|
|
{
|
|
allDay: false,
|
|
description: 'Stateful browser mock check',
|
|
endsAt: 2_000_003_600,
|
|
location: 'Legion Square',
|
|
reminderMinutes: 15,
|
|
startsAt: 2_000_000_000,
|
|
title: 'Browser test event',
|
|
},
|
|
true,
|
|
)
|
|
let events = await expectSuccess(
|
|
baseUrl,
|
|
'calendar:list',
|
|
{ endsAt: 4_102_444_800, startsAt: 0 },
|
|
true,
|
|
)
|
|
const storedEvent = events.find((item) => item.id === event.id)
|
|
assert(storedEvent, 'calendar:create did not persist')
|
|
await expectSuccess(baseUrl, 'calendar:update', {
|
|
...storedEvent,
|
|
endsAt: storedEvent.endsAt / 1000,
|
|
startsAt: storedEvent.startsAt / 1000,
|
|
title: 'Updated browser test event',
|
|
})
|
|
events = await expectSuccess(
|
|
baseUrl,
|
|
'calendar:list',
|
|
{ endsAt: 4_102_444_800, startsAt: 0 },
|
|
true,
|
|
)
|
|
assert.equal(
|
|
events.find((item) => item.id === event.id)?.title,
|
|
'Updated browser test event',
|
|
)
|
|
await expectSuccess(baseUrl, 'calendar:delete', { id: event.id })
|
|
|
|
const marker = await expectSuccess(
|
|
baseUrl,
|
|
'map:create-marker',
|
|
{
|
|
color: '#2dd4bf',
|
|
coords: { x: 215.2, y: -810.1, z: 30.7 },
|
|
icon: 'pin',
|
|
label: 'Browser test marker',
|
|
},
|
|
true,
|
|
)
|
|
let markers = await expectSuccess(baseUrl, 'map:markers', {}, true)
|
|
assert(
|
|
markers.some((item) => item.id === marker.id),
|
|
'map:create-marker did not persist',
|
|
)
|
|
await expectSuccess(baseUrl, 'map:delete-marker', { id: marker.id })
|
|
markers = await expectSuccess(baseUrl, 'map:markers', {}, true)
|
|
assert(
|
|
!markers.some((item) => item.id === marker.id),
|
|
'map:delete-marker did not persist',
|
|
)
|
|
|
|
const bankingBefore = await expectSuccess(
|
|
baseUrl,
|
|
'banking:overview',
|
|
{},
|
|
true,
|
|
)
|
|
const bankingAfter = await expectSuccess(
|
|
baseUrl,
|
|
'banking:transfer',
|
|
{ amount: 125, phoneNumber: '5551110001' },
|
|
true,
|
|
)
|
|
assert.equal(bankingAfter.bank, bankingBefore.bank - 125)
|
|
|
|
const health = await expectSuccess(baseUrl, 'health:overview', {}, true)
|
|
assert.equal(health.dailyStepGoal, 8000)
|
|
assert.equal(health.days.length, 7)
|
|
assert.equal(health.days.at(-1).steps, 6420)
|
|
assert.equal('snapshot' in health, false)
|
|
|
|
const medicalId = await expectSuccess(
|
|
baseUrl,
|
|
'health:save-profile',
|
|
{
|
|
allergies: 'Penicillin',
|
|
bloodType: 'A+',
|
|
conditions: 'Asthma',
|
|
emergencyName: 'Jamie Morgan',
|
|
emergencyPhone: '5550102211',
|
|
emergencyRelation: 'Sibling',
|
|
medication: 'Inhaler',
|
|
},
|
|
true,
|
|
)
|
|
assert.equal(medicalId.bloodType, 'A+')
|
|
assert.equal(medicalId.allergies, 'Penicillin')
|
|
|
|
let radio = await expectSuccess(
|
|
baseUrl,
|
|
'radio:connect',
|
|
{ frequency: 42.5, secondaryFrequency: 7.25 },
|
|
true,
|
|
)
|
|
assert.equal(radio.frequency, 42.5)
|
|
radio = await expectSuccess(baseUrl, 'radio:set-volume', { volume: 44 }, true)
|
|
assert.equal(radio.volume, 44)
|
|
radio = await expectSuccess(
|
|
baseUrl,
|
|
'radio:set-speaker',
|
|
{ enabled: true },
|
|
true,
|
|
)
|
|
assert.equal(radio.speakerEnabled, true)
|
|
await expectSuccess(baseUrl, 'radio:disconnect')
|
|
|
|
const playlistState = await expectSuccess(
|
|
baseUrl,
|
|
'music:create-playlist',
|
|
{ name: 'Browser Test Mix' },
|
|
true,
|
|
)
|
|
const playlist = playlistState.playlists.find(
|
|
(item) => item.name === 'Browser Test Mix',
|
|
)
|
|
assert(playlist, 'music:create-playlist did not persist')
|
|
await expectSuccess(
|
|
baseUrl,
|
|
'music:rename-playlist',
|
|
{ id: playlist.id, name: 'Updated Browser Mix' },
|
|
true,
|
|
)
|
|
await expectSuccess(
|
|
baseUrl,
|
|
'music:delete-playlist',
|
|
{ id: playlist.id },
|
|
true,
|
|
)
|
|
|
|
await expectSuccess(baseUrl, 'sim:eject')
|
|
let bootstrap = await expectSuccess(
|
|
baseUrl,
|
|
'development:bootstrap',
|
|
{},
|
|
true,
|
|
)
|
|
assert.equal(bootstrap.device.sim, null)
|
|
const simConfirmation = await post(baseUrl, 'sim:insert', {
|
|
imei: '356938035643810',
|
|
})
|
|
assert.deepEqual(simConfirmation, {
|
|
error: 'confirmation_required',
|
|
success: false,
|
|
})
|
|
await expectSuccess(baseUrl, 'sim:insert', {
|
|
confirmed: true,
|
|
imei: '356938035643810',
|
|
})
|
|
bootstrap = await expectSuccess(baseUrl, 'development:bootstrap', {}, true)
|
|
assert.equal(bootstrap.device.sim.number, '5559876543')
|
|
|
|
const payphoneCall = await expectSuccess(
|
|
baseUrl,
|
|
'payphone:dial',
|
|
{ phoneNumber: '5551110001' },
|
|
true,
|
|
)
|
|
assert.equal(payphoneCall.state, 'connected')
|
|
await expectSuccess(baseUrl, 'payphone:hangup')
|
|
|
|
const mailbox = await expectSuccess(
|
|
baseUrl,
|
|
'mail:create-mailbox',
|
|
{ name: 'Browser Test' },
|
|
true,
|
|
)
|
|
const mailboxes = await expectSuccess(
|
|
baseUrl,
|
|
'mail:mailboxes',
|
|
{},
|
|
true,
|
|
)
|
|
assert(
|
|
mailboxes.mailboxes.some((item) => item.id === mailbox.id),
|
|
'mail:create-mailbox did not update the mock mailbox list',
|
|
)
|
|
await expectSuccess(baseUrl, 'mail:move', {
|
|
id: 2,
|
|
mailboxId: mailbox.id,
|
|
})
|
|
const mailboxItems = await expectSuccess(
|
|
baseUrl,
|
|
'mail:list',
|
|
{ folder: `mailbox:${mailbox.id}` },
|
|
true,
|
|
)
|
|
assert(
|
|
mailboxItems.items.some((message) => message.id === 2),
|
|
'mail:move did not add the message to the custom mailbox',
|
|
)
|
|
await expectSuccess(baseUrl, 'mail:move', { id: 2, mailboxId: 0 })
|
|
await expectSuccess(baseUrl, 'mail:delete-mailbox', { id: mailbox.id })
|
|
|
|
const draft = await expectSuccess(
|
|
baseUrl,
|
|
'mail:save-draft',
|
|
{
|
|
body: 'Browser test body',
|
|
recipients: ['alex@ifruit.com'],
|
|
subject: 'Browser test mail',
|
|
},
|
|
true,
|
|
)
|
|
const storedDraft = await expectSuccess(
|
|
baseUrl,
|
|
'mail:get-draft',
|
|
{ id: draft.id },
|
|
true,
|
|
)
|
|
assert.equal(storedDraft.subject, 'Browser test mail')
|
|
await expectSuccess(baseUrl, 'mail:delete-many', {
|
|
folder: 'drafts',
|
|
ids: [draft.id],
|
|
})
|
|
const drafts = await expectSuccess(
|
|
baseUrl,
|
|
'mail:list',
|
|
{ folder: 'drafts' },
|
|
true,
|
|
)
|
|
assert.equal(drafts.items.length, 0, 'mail:delete-many kept selected draft')
|
|
|
|
await expectSuccess(baseUrl, 'mail:delete-many', {
|
|
folder: 'inbox',
|
|
ids: [1],
|
|
})
|
|
const inbox = await expectSuccess(
|
|
baseUrl,
|
|
'mail:list',
|
|
{ folder: 'inbox' },
|
|
true,
|
|
)
|
|
assert(
|
|
inbox.items.some((message) => message.id === 2),
|
|
'mail:delete-many removed a non-selected inbox message',
|
|
)
|
|
const trash = await expectSuccess(
|
|
baseUrl,
|
|
'mail:list',
|
|
{ folder: 'trash' },
|
|
true,
|
|
)
|
|
assert(
|
|
trash.items.some((message) => message.id === 1),
|
|
'mail:delete-many did not move selected inbox mail to trash',
|
|
)
|
|
|
|
await expectSuccess(baseUrl, 'mail:delete-many', {
|
|
folder: 'sent',
|
|
ids: [3],
|
|
})
|
|
const sent = await expectSuccess(
|
|
baseUrl,
|
|
'mail:list',
|
|
{ folder: 'sent' },
|
|
true,
|
|
)
|
|
assert.equal(sent.items.length, 0, 'mail:delete-many kept selected sent mail')
|
|
|
|
await expectSuccess(baseUrl, 'mail:delete-many', {
|
|
folder: 'trash',
|
|
ids: [1],
|
|
})
|
|
const remainingTrash = await expectSuccess(
|
|
baseUrl,
|
|
'mail:list',
|
|
{ folder: 'trash' },
|
|
true,
|
|
)
|
|
assert(
|
|
!remainingTrash.items.some((message) => message.id === 1),
|
|
'mail:delete-many kept permanently deleted trash mail',
|
|
)
|
|
assert(
|
|
remainingTrash.items.some((message) => message.id === 4),
|
|
'mail:delete-many removed non-selected trash mail',
|
|
)
|
|
|
|
const flareBeforeDelete = await expectSuccess(
|
|
baseUrl,
|
|
'flare:bootstrap',
|
|
{},
|
|
true,
|
|
)
|
|
assert(flareBeforeDelete.profile, 'flare:bootstrap did not include a profile')
|
|
assert(
|
|
flareBeforeDelete.matches.length > 0,
|
|
'flare:bootstrap did not include a deletable match',
|
|
)
|
|
const swipedProfile = flareBeforeDelete.suggestions[0]
|
|
await expectSuccess(baseUrl, 'flare:swipe', {
|
|
choice: 'pass',
|
|
targetId: swipedProfile.id,
|
|
})
|
|
const flareAfterSwipe = await expectSuccess(
|
|
baseUrl,
|
|
'flare:bootstrap',
|
|
{},
|
|
true,
|
|
)
|
|
assert(
|
|
!flareAfterSwipe.suggestions.some(
|
|
(profile) => profile.id === swipedProfile.id,
|
|
),
|
|
'flare:swipe did not filter the swiped profile',
|
|
)
|
|
await expectSuccess(baseUrl, 'flare:delete-profile')
|
|
const flareAfterDelete = await expectSuccess(
|
|
baseUrl,
|
|
'flare:bootstrap',
|
|
{},
|
|
true,
|
|
)
|
|
assert.equal(flareAfterDelete.profile, null)
|
|
assert.deepEqual(flareAfterDelete.suggestions, [])
|
|
assert.deepEqual(flareAfterDelete.likes, [])
|
|
assert.deepEqual(flareAfterDelete.matches, [])
|
|
const repeatedFlareDelete = await post(baseUrl, 'flare:delete-profile')
|
|
assert.deepEqual(repeatedFlareDelete, {
|
|
error: 'profile_not_found',
|
|
success: false,
|
|
})
|
|
const recreatedFlare = await expectSuccess(
|
|
baseUrl,
|
|
'flare:save-profile',
|
|
{
|
|
...flareBeforeDelete.profile,
|
|
photoMediaIds: [],
|
|
photoUrls: undefined,
|
|
},
|
|
true,
|
|
)
|
|
assert(
|
|
recreatedFlare.suggestions.some(
|
|
(profile) => profile.id === swipedProfile.id,
|
|
),
|
|
'recreated Flare profile retained a deleted swipe filter',
|
|
)
|
|
|
|
await expectSuccess(baseUrl, 'account:logout')
|
|
const signedOutFlareBootstrap = await post(baseUrl, 'flare:bootstrap')
|
|
assert.deepEqual(signedOutFlareBootstrap, {
|
|
error: 'not_authenticated',
|
|
success: false,
|
|
})
|
|
const signedOutFlareDelete = await post(baseUrl, 'flare:delete-profile')
|
|
assert.deepEqual(signedOutFlareDelete, {
|
|
error: 'not_authenticated',
|
|
success: false,
|
|
})
|
|
}
|
|
|
|
async function main() {
|
|
const server = app.listen(0, '127.0.0.1')
|
|
await once(server, 'listening')
|
|
const address = server.address()
|
|
const baseUrl = `http://127.0.0.1:${address.port}`
|
|
|
|
try {
|
|
const dataByEndpoint = new Map()
|
|
for (const [endpoint, body] of browserDataRequests) {
|
|
dataByEndpoint.set(
|
|
endpoint,
|
|
await expectSuccess(baseUrl, endpoint, body, true),
|
|
)
|
|
}
|
|
verifyBrowserTestData(dataByEndpoint)
|
|
|
|
await verifyStatefulActions(baseUrl)
|
|
|
|
const lifecycleEndpoints = [
|
|
'camera:setActive',
|
|
'camera:setLocked',
|
|
'camera:setFacing',
|
|
'camera:setFlash',
|
|
'camera:setFocus',
|
|
'camera:setOrientation',
|
|
'camera:setZoom',
|
|
'close',
|
|
'custom-app:lifecycle',
|
|
'device:notification-open',
|
|
'notification:focus',
|
|
'sim:picker-close',
|
|
'ui:opened',
|
|
'ui:ready',
|
|
]
|
|
for (const endpoint of lifecycleEndpoints) {
|
|
await expectSuccess(baseUrl, endpoint)
|
|
}
|
|
|
|
const unknown = await post(baseUrl, 'development:missing-mock', {})
|
|
assert.deepEqual(unknown, {
|
|
error: 'mock_endpoint_missing',
|
|
success: false,
|
|
})
|
|
} finally {
|
|
await new Promise((resolve, reject) => {
|
|
server.close((error) => (error ? reject(error) : resolve()))
|
|
})
|
|
}
|
|
|
|
console.log(
|
|
`Verified ${browserDataRequests.length} browser data endpoints and stateful app actions.`,
|
|
)
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error)
|
|
process.exitCode = 1
|
|
})
|