@prenta/admin 2.7.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/design-system/tokens.css +17 -0
- package/design-system/tokens.json +35 -1
- package/design-system/tokens.scss +15 -0
- package/design-system/tokens.tailwind.cjs +14 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +2 -2
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/lib/batch-wizard.test.d.ts +2 -0
- package/dist/__tests__/lib/batch-wizard.test.d.ts.map +1 -0
- package/dist/__tests__/lib/batch-wizard.test.js +144 -0
- package/dist/__tests__/lib/batch-wizard.test.js.map +1 -0
- package/dist/__tests__/lib/job-drawer.test.js +2 -0
- package/dist/__tests__/lib/job-drawer.test.js.map +1 -1
- package/dist/__tests__/lib/job-view.test.js +100 -3
- package/dist/__tests__/lib/job-view.test.js.map +1 -1
- package/dist/__tests__/lib/jobs-service.test.js +56 -2
- package/dist/__tests__/lib/jobs-service.test.js.map +1 -1
- package/dist/__tests__/views/appearance-chrome.render.test.d.ts +2 -0
- package/dist/__tests__/views/appearance-chrome.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/appearance-chrome.render.test.js +688 -0
- package/dist/__tests__/views/appearance-chrome.render.test.js.map +1 -0
- package/dist/__tests__/views/header-footer-form.test.d.ts +2 -0
- package/dist/__tests__/views/header-footer-form.test.d.ts.map +1 -0
- package/dist/__tests__/views/header-footer-form.test.js +266 -0
- package/dist/__tests__/views/header-footer-form.test.js.map +1 -0
- package/dist/__tests__/views/jobs-drawer.render.test.js +442 -2
- package/dist/__tests__/views/jobs-drawer.render.test.js.map +1 -1
- package/dist/__tests__/views/jobs-list.render.test.js +7 -5
- package/dist/__tests__/views/jobs-list.render.test.js.map +1 -1
- package/dist/__tests__/views/jobs-wizard.render.test.d.ts +2 -0
- package/dist/__tests__/views/jobs-wizard.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/jobs-wizard.render.test.js +588 -0
- package/dist/__tests__/views/jobs-wizard.render.test.js.map +1 -0
- package/dist/lib/api.d.ts +2 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +8 -1
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/batch-wizard.d.ts +84 -0
- package/dist/lib/batch-wizard.d.ts.map +1 -0
- package/dist/lib/batch-wizard.js +122 -0
- package/dist/lib/batch-wizard.js.map +1 -0
- package/dist/lib/chrome-service.d.ts +69 -0
- package/dist/lib/chrome-service.d.ts.map +1 -0
- package/dist/lib/chrome-service.js +108 -0
- package/dist/lib/chrome-service.js.map +1 -0
- package/dist/lib/job-view.d.ts +43 -0
- package/dist/lib/job-view.d.ts.map +1 -1
- package/dist/lib/job-view.js +57 -1
- package/dist/lib/job-view.js.map +1 -1
- package/dist/lib/jobs-service.d.ts +19 -1
- package/dist/lib/jobs-service.d.ts.map +1 -1
- package/dist/lib/jobs-service.js +41 -0
- package/dist/lib/jobs-service.js.map +1 -1
- package/dist/prenta-admin.css +1 -1
- package/dist/views/AppearanceChrome.d.ts +22 -4
- package/dist/views/AppearanceChrome.d.ts.map +1 -1
- package/dist/views/AppearanceChrome.js +451 -15
- package/dist/views/AppearanceChrome.js.map +1 -1
- package/dist/views/Jobs/CancelJobDialog.d.ts +7 -1
- package/dist/views/Jobs/CancelJobDialog.d.ts.map +1 -1
- package/dist/views/Jobs/CancelJobDialog.js +13 -3
- package/dist/views/Jobs/CancelJobDialog.js.map +1 -1
- package/dist/views/Jobs/DrawerReview.d.ts +7 -0
- package/dist/views/Jobs/DrawerReview.d.ts.map +1 -1
- package/dist/views/Jobs/DrawerReview.js +1 -1
- package/dist/views/Jobs/DrawerReview.js.map +1 -1
- package/dist/views/Jobs/JobDrawer.d.ts +23 -3
- package/dist/views/Jobs/JobDrawer.d.ts.map +1 -1
- package/dist/views/Jobs/JobDrawer.js +30 -5
- package/dist/views/Jobs/JobDrawer.js.map +1 -1
- package/dist/views/Jobs/JobsTable.d.ts +4 -2
- package/dist/views/Jobs/JobsTable.d.ts.map +1 -1
- package/dist/views/Jobs/JobsTable.js +6 -5
- package/dist/views/Jobs/JobsTable.js.map +1 -1
- package/dist/views/Jobs/NewBatchJob.d.ts +34 -0
- package/dist/views/Jobs/NewBatchJob.d.ts.map +1 -0
- package/dist/views/Jobs/NewBatchJob.js +282 -0
- package/dist/views/Jobs/NewBatchJob.js.map +1 -0
- package/dist/views/Jobs/WorkerStrip.d.ts +3 -1
- package/dist/views/Jobs/WorkerStrip.d.ts.map +1 -1
- package/dist/views/Jobs/WorkerStrip.js +2 -2
- package/dist/views/Jobs/WorkerStrip.js.map +1 -1
- package/dist/views/Jobs/focus.d.ts +6 -0
- package/dist/views/Jobs/focus.d.ts.map +1 -1
- package/dist/views/Jobs/focus.js +10 -0
- package/dist/views/Jobs/focus.js.map +1 -1
- package/dist/views/Jobs.d.ts +12 -3
- package/dist/views/Jobs.d.ts.map +1 -1
- package/dist/views/Jobs.js +109 -31
- package/dist/views/Jobs.js.map +1 -1
- package/dist/views/header-footer/AnnouncementTab.d.ts +15 -0
- package/dist/views/header-footer/AnnouncementTab.d.ts.map +1 -0
- package/dist/views/header-footer/AnnouncementTab.js +43 -0
- package/dist/views/header-footer/AnnouncementTab.js.map +1 -0
- package/dist/views/header-footer/ChromeField.d.ts +25 -0
- package/dist/views/header-footer/ChromeField.d.ts.map +1 -0
- package/dist/views/header-footer/ChromeField.js +99 -0
- package/dist/views/header-footer/ChromeField.js.map +1 -0
- package/dist/views/header-footer/ChromePreview.d.ts +31 -0
- package/dist/views/header-footer/ChromePreview.d.ts.map +1 -0
- package/dist/views/header-footer/ChromePreview.js +195 -0
- package/dist/views/header-footer/ChromePreview.js.map +1 -0
- package/dist/views/header-footer/DeveloperTab.d.ts +17 -0
- package/dist/views/header-footer/DeveloperTab.d.ts.map +1 -0
- package/dist/views/header-footer/DeveloperTab.js +98 -0
- package/dist/views/header-footer/DeveloperTab.js.map +1 -0
- package/dist/views/header-footer/RegionTab.d.ts +27 -0
- package/dist/views/header-footer/RegionTab.d.ts.map +1 -0
- package/dist/views/header-footer/RegionTab.js +80 -0
- package/dist/views/header-footer/RegionTab.js.map +1 -0
- package/dist/views/header-footer/chrome-form.d.ts +137 -0
- package/dist/views/header-footer/chrome-form.d.ts.map +1 -0
- package/dist/views/header-footer/chrome-form.js +518 -0
- package/dist/views/header-footer/chrome-form.js.map +1 -0
- package/dist/views/header-footer/code-samples.d.ts +30 -0
- package/dist/views/header-footer/code-samples.d.ts.map +1 -0
- package/dist/views/header-footer/code-samples.js +127 -0
- package/dist/views/header-footer/code-samples.js.map +1 -0
- package/dist/views/header-footer/controls.d.ts +57 -0
- package/dist/views/header-footer/controls.d.ts.map +1 -0
- package/dist/views/header-footer/controls.js +43 -0
- package/dist/views/header-footer/controls.js.map +1 -0
- package/package.json +3 -3
- package/src/AdminRoot.tsx +16 -2
- package/src/__tests__/lib/batch-wizard.test.ts +199 -0
- package/src/__tests__/lib/job-drawer.test.ts +2 -0
- package/src/__tests__/lib/job-view.test.ts +123 -2
- package/src/__tests__/lib/jobs-service.test.ts +72 -3
- package/src/__tests__/views/appearance-chrome.render.test.tsx +818 -0
- package/src/__tests__/views/header-footer-form.test.ts +333 -0
- package/src/__tests__/views/jobs-drawer.render.test.tsx +508 -2
- package/src/__tests__/views/jobs-list.render.test.tsx +8 -5
- package/src/__tests__/views/jobs-wizard.render.test.tsx +707 -0
- package/src/lib/api.ts +12 -1
- package/src/lib/batch-wizard.ts +188 -0
- package/src/lib/chrome-service.ts +200 -0
- package/src/lib/job-view.ts +95 -1
- package/src/lib/jobs-service.ts +56 -1
- package/src/styles/prenta-tokens.css +17 -0
- package/src/styles/theme.css +15 -0
- package/src/views/AppearanceChrome.tsx +704 -46
- package/src/views/Jobs/CancelJobDialog.tsx +20 -2
- package/src/views/Jobs/DrawerReview.tsx +1 -1
- package/src/views/Jobs/JobDrawer.tsx +83 -3
- package/src/views/Jobs/JobsTable.tsx +11 -4
- package/src/views/Jobs/NewBatchJob.tsx +857 -0
- package/src/views/Jobs/WorkerStrip.tsx +16 -11
- package/src/views/Jobs/focus.ts +11 -0
- package/src/views/Jobs.tsx +163 -49
- package/src/views/header-footer/AnnouncementTab.tsx +231 -0
- package/src/views/header-footer/ChromeField.tsx +358 -0
- package/src/views/header-footer/ChromePreview.tsx +497 -0
- package/src/views/header-footer/DeveloperTab.tsx +331 -0
- package/src/views/header-footer/RegionTab.tsx +398 -0
- package/src/views/header-footer/chrome-form.ts +603 -0
- package/src/views/header-footer/code-samples.ts +146 -0
- package/src/views/header-footer/controls.tsx +166 -0
- package/dist/views/Jobs/placeholders.d.ts +0 -6
- package/dist/views/Jobs/placeholders.d.ts.map +0 -1
- package/dist/views/Jobs/placeholders.js +0 -20
- package/dist/views/Jobs/placeholders.js.map +0 -1
- package/src/views/Jobs/placeholders.tsx +0 -66
|
@@ -2,17 +2,22 @@ import { describe, expect, it } from 'vitest'
|
|
|
2
2
|
import type { Job, JobWorkerStatus } from '@prenta/core'
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
canActOnJobs,
|
|
6
|
+
canRetryJob,
|
|
5
7
|
countJobs,
|
|
6
8
|
formatCreated,
|
|
7
9
|
formatDuration,
|
|
8
10
|
formatNextRun,
|
|
9
11
|
formatSince,
|
|
10
12
|
hasReviewAction,
|
|
13
|
+
jobActions,
|
|
11
14
|
matchJob,
|
|
15
|
+
retryPoint,
|
|
12
16
|
scheduleLabel,
|
|
13
17
|
toJobRows,
|
|
14
18
|
view,
|
|
15
19
|
type JobRow,
|
|
20
|
+
type JobViewer,
|
|
16
21
|
} from '../../lib/job-view.js'
|
|
17
22
|
|
|
18
23
|
function row(overrides: Partial<JobRow>): JobRow {
|
|
@@ -35,8 +40,10 @@ function row(overrides: Partial<JobRow>): JobRow {
|
|
|
35
40
|
pending: 0,
|
|
36
41
|
items: false,
|
|
37
42
|
review: null,
|
|
43
|
+
adminRetry: false,
|
|
38
44
|
took: null,
|
|
39
45
|
by: 'Brandon West',
|
|
46
|
+
byId: null,
|
|
40
47
|
created: 'now',
|
|
41
48
|
...overrides,
|
|
42
49
|
}
|
|
@@ -386,11 +393,125 @@ describe('toJobRows()', () => {
|
|
|
386
393
|
review: null,
|
|
387
394
|
created: 'Today 12:00',
|
|
388
395
|
})
|
|
389
|
-
expect(cron).toMatchObject({ cancelledBy: 'Dana Cole', by: 'Dana Cole' })
|
|
390
|
-
expect(nightly).toMatchObject({ by: 'Nightly cron', created: 'Yesterday 12:00' })
|
|
396
|
+
expect(cron).toMatchObject({ cancelledBy: 'Dana Cole', by: 'Dana Cole', byId: 'u2' })
|
|
397
|
+
expect(nightly).toMatchObject({ by: 'Nightly cron', byId: null, created: 'Yesterday 12:00' })
|
|
391
398
|
expect(review).toMatchObject({ pending: 2, items: true })
|
|
392
399
|
expect(review && view(review).l).toBe('Needs review')
|
|
393
400
|
})
|
|
401
|
+
|
|
402
|
+
it('counts a batch that hasn’t started by its frozen documents', () => {
|
|
403
|
+
const fresh = (type: string, ids: string[]) =>
|
|
404
|
+
apiJob({
|
|
405
|
+
type,
|
|
406
|
+
progress: { done: 0, total: 0, unit: 'documents', cursor: null, completed: 0 },
|
|
407
|
+
payload: { ...(apiJob({}).payload as object), documentIds: ids } as Job['payload'],
|
|
408
|
+
})
|
|
409
|
+
const [batch, scan, started] = toJobRows(
|
|
410
|
+
[
|
|
411
|
+
fresh('batch-ai', ['a', 'b', 'c']),
|
|
412
|
+
fresh('seo-scan', ['a']),
|
|
413
|
+
apiJob({ progress: { done: 2, total: 5, unit: 'documents', cursor: '2', completed: 2 } }),
|
|
414
|
+
],
|
|
415
|
+
NOW,
|
|
416
|
+
null,
|
|
417
|
+
)
|
|
418
|
+
expect([batch?.total, scan?.total, started?.total]).toEqual([3, 0, 5])
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
it('makes only a stored "Apply automatically" admin-only to retry, as cms-core does', () => {
|
|
422
|
+
const batch = (review: 'review' | 'auto', origin?: 'ai-batch') =>
|
|
423
|
+
apiJob({
|
|
424
|
+
payload: {
|
|
425
|
+
...(apiJob({}).payload as object),
|
|
426
|
+
review,
|
|
427
|
+
...(origin ? { origin } : {}),
|
|
428
|
+
} as Job['payload'],
|
|
429
|
+
})
|
|
430
|
+
const [review, auto, bulk] = toJobRows(
|
|
431
|
+
[batch('review'), batch('auto'), batch('auto', 'ai-batch')],
|
|
432
|
+
NOW,
|
|
433
|
+
null,
|
|
434
|
+
)
|
|
435
|
+
expect([review?.adminRetry, auto?.adminRetry, bulk?.adminRetry]).toEqual([false, true, false])
|
|
436
|
+
// The bulk action still reads as applying directly, for the cancel copy.
|
|
437
|
+
expect(bulk?.review).toBe('auto')
|
|
438
|
+
})
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
const as = (role: string | null, userId: string | null = 'u1'): JobViewer => ({ role, userId })
|
|
442
|
+
|
|
443
|
+
describe('job actions (P-19)', () => {
|
|
444
|
+
it('lets the roles core calls writers run and cancel, and nobody else', () => {
|
|
445
|
+
for (const role of ['OWNER', 'ADMIN', 'EDITOR', 'AUTHOR', 'editor'])
|
|
446
|
+
expect(canActOnJobs(as(role))).toBe(true)
|
|
447
|
+
for (const role of ['VIEWER', 'CLIENT', 'SOMETHING_NEW', null])
|
|
448
|
+
expect(canActOnJobs(as(role))).toBe(false)
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
it('retries a batch for an admin, or for the editor who queued it unless it was "Apply automatically"', () => {
|
|
452
|
+
const mine = row({ review: 'review', byId: 'u1' })
|
|
453
|
+
const theirs = row({ review: 'review', byId: 'u2' })
|
|
454
|
+
const auto = row({ review: 'auto', adminRetry: true, byId: 'u1' })
|
|
455
|
+
// A list's bulk action (`/ai/batch`): it applies directly, but any editor may queue it.
|
|
456
|
+
const legacy = row({ review: 'auto', adminRetry: false, byId: 'u1' })
|
|
457
|
+
const scan = row({ type: 'seo-scan', review: null, byId: null })
|
|
458
|
+
expect([mine, theirs, auto, legacy, scan].map((j) => canRetryJob(j, as('ADMIN')))).toEqual([
|
|
459
|
+
true,
|
|
460
|
+
true,
|
|
461
|
+
true,
|
|
462
|
+
true,
|
|
463
|
+
true,
|
|
464
|
+
])
|
|
465
|
+
expect([mine, theirs, auto, legacy, scan].map((j) => canRetryJob(j, as('EDITOR')))).toEqual([
|
|
466
|
+
true,
|
|
467
|
+
false,
|
|
468
|
+
false,
|
|
469
|
+
true,
|
|
470
|
+
true,
|
|
471
|
+
])
|
|
472
|
+
expect(canRetryJob(mine, as('VIEWER'))).toBe(false)
|
|
473
|
+
expect(canRetryJob(row({ byId: null }), as('EDITOR', null))).toBe(false)
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
it('offers the drawer footer’s actions per state (jobs-drawer.jsx `.jd-f`)', () => {
|
|
477
|
+
const admin = as('ADMIN')
|
|
478
|
+
const actions = (overrides: Partial<JobRow>) => jobActions(row(overrides), admin)
|
|
479
|
+
expect(actions({ state: 'running' })).toEqual({ cancel: true, retry: false, retryNow: false })
|
|
480
|
+
expect(actions({ state: 'running', cancelling: true })).toEqual({
|
|
481
|
+
cancel: false,
|
|
482
|
+
retry: false,
|
|
483
|
+
retryNow: false,
|
|
484
|
+
})
|
|
485
|
+
expect(actions({ state: 'queued', error: 'Boom' })).toEqual({
|
|
486
|
+
cancel: true,
|
|
487
|
+
retry: false,
|
|
488
|
+
retryNow: true,
|
|
489
|
+
})
|
|
490
|
+
// A retry clears a cancel, so a cancelling job doesn't offer it.
|
|
491
|
+
expect(actions({ state: 'queued', error: 'Boom', cancelling: true }).retryNow).toBe(false)
|
|
492
|
+
// A stale lock: the server still holds it as running and would refuse a retry.
|
|
493
|
+
expect(
|
|
494
|
+
actions({ state: 'queued', status: 'running', error: 'Worker stopped mid-slice' }),
|
|
495
|
+
).toEqual({ cancel: true, retry: false, retryNow: false })
|
|
496
|
+
expect(actions({ state: 'dead', error: 'Boom' })).toEqual({
|
|
497
|
+
cancel: false,
|
|
498
|
+
retry: true,
|
|
499
|
+
retryNow: false,
|
|
500
|
+
})
|
|
501
|
+
for (const state of ['succeeded', 'cancelled'] as const)
|
|
502
|
+
expect(actions({ state })).toEqual({ cancel: false, retry: false, retryNow: false })
|
|
503
|
+
expect(jobActions(row({ state: 'dead' }), as('VIEWER'))).toEqual({
|
|
504
|
+
cancel: false,
|
|
505
|
+
retry: false,
|
|
506
|
+
retryNow: false,
|
|
507
|
+
})
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
it('says where a retry picks up, in pages for an SEO scan', () => {
|
|
511
|
+
expect(retryPoint({ type: 'batch-ai', done: 7 })).toBe('document 8')
|
|
512
|
+
expect(retryPoint({ type: 'content-health', done: 0 })).toBe('document 1')
|
|
513
|
+
expect(retryPoint({ type: 'seo-scan', done: 100 })).toBe('page 101')
|
|
514
|
+
})
|
|
394
515
|
})
|
|
395
516
|
|
|
396
517
|
describe('formatting', () => {
|
|
@@ -3,17 +3,86 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
3
3
|
/*
|
|
4
4
|
* The drawer's calls in the Jobs service: the review page query, the
|
|
5
5
|
* decision envelope's `documentStatus` and `notice`, and reading why a
|
|
6
|
-
* decision was refused.
|
|
6
|
+
* decision was refused. And the batch wizard's: estimate, preview, create.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const cmsApi = vi.hoisted(() => vi.fn())
|
|
10
10
|
vi.mock('../../lib/api.js', () => ({ cmsApi, getApiBase: () => '/api/cms' }))
|
|
11
11
|
|
|
12
|
-
const {
|
|
13
|
-
|
|
12
|
+
const {
|
|
13
|
+
createBatchAiJob,
|
|
14
|
+
decideReviewItem,
|
|
15
|
+
estimateBatchAi,
|
|
16
|
+
listReviewItems,
|
|
17
|
+
previewBatchAi,
|
|
18
|
+
reviewConflict,
|
|
19
|
+
} = await import('../../lib/jobs-service.js')
|
|
14
20
|
|
|
15
21
|
beforeEach(() => cmsApi.mockReset())
|
|
16
22
|
|
|
23
|
+
describe('batch AI', () => {
|
|
24
|
+
const request = {
|
|
25
|
+
operation: 'custom' as const,
|
|
26
|
+
source: 'posts',
|
|
27
|
+
statusFilter: 'all' as const,
|
|
28
|
+
review: 'review' as const,
|
|
29
|
+
onApprove: 'draft' as const,
|
|
30
|
+
instruction: 'Translate it & keep names',
|
|
31
|
+
field: 'excerpt' as const,
|
|
32
|
+
filter: { documentIds: ['a', 'b'] },
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
it('estimates with the request as the query, ids comma-separated', async () => {
|
|
36
|
+
const value = { count: 2, sample: [], tokens: 1800, minutes: 2, pctOfMonthlyLimit: null }
|
|
37
|
+
cmsApi.mockResolvedValue({ data: value, status: 200 })
|
|
38
|
+
expect(await estimateBatchAi(request)).toEqual({ ok: true, value })
|
|
39
|
+
const [endpoint] = cmsApi.mock.calls[0]!
|
|
40
|
+
expect(endpoint).toBe(
|
|
41
|
+
'/jobs/batch-ai/estimate?operation=custom&source=posts&statusFilter=all&field=excerpt&instruction=Translate+it+%26+keep+names&documentIds=a%2Cb',
|
|
42
|
+
)
|
|
43
|
+
cmsApi.mockClear()
|
|
44
|
+
await estimateBatchAi({
|
|
45
|
+
...request,
|
|
46
|
+
operation: 'meta',
|
|
47
|
+
field: undefined,
|
|
48
|
+
instruction: null,
|
|
49
|
+
filter: undefined,
|
|
50
|
+
})
|
|
51
|
+
expect(cmsApi.mock.calls[0]![0]).toBe(
|
|
52
|
+
'/jobs/batch-ai/estimate?operation=meta&source=posts&statusFilter=all',
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('previews with a POST, reading the items', async () => {
|
|
57
|
+
const items = [{ documentId: 'a', title: 'A', after: 'New text.' }]
|
|
58
|
+
cmsApi.mockResolvedValue({ data: { items }, status: 200 })
|
|
59
|
+
expect(await previewBatchAi(request)).toEqual({ ok: true, value: items })
|
|
60
|
+
expect(cmsApi.mock.calls[0]![0]).toBe('/jobs/batch-ai/preview')
|
|
61
|
+
expect(cmsApi.mock.calls[0]![1]).toMatchObject({
|
|
62
|
+
method: 'POST',
|
|
63
|
+
body: JSON.stringify(request),
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('passes a spent cap on', async () => {
|
|
68
|
+
cmsApi.mockResolvedValue({ error: 'AI limit reached', status: 429 })
|
|
69
|
+
expect(await previewBatchAi(request)).toEqual({
|
|
70
|
+
ok: false,
|
|
71
|
+
error: 'AI limit reached',
|
|
72
|
+
status: 429,
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('creates with a POST and returns the job', async () => {
|
|
77
|
+
cmsApi.mockResolvedValue({ data: { id: 'job_420' }, status: 201 })
|
|
78
|
+
expect(await createBatchAiJob(request)).toEqual({ ok: true, value: { id: 'job_420' } })
|
|
79
|
+
expect(cmsApi).toHaveBeenCalledWith('/jobs/batch-ai', {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
body: JSON.stringify(request),
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
17
86
|
describe('listReviewItems', () => {
|
|
18
87
|
it('asks for 20 at a time, from a cursor', async () => {
|
|
19
88
|
cmsApi.mockResolvedValue({ data: { items: [], nextCursor: null }, status: 200 })
|