diff --git a/e2e/import-document.spec.ts b/e2e/import-document.spec.ts index 0c40811..8e9c93b 100644 --- a/e2e/import-document.spec.ts +++ b/e2e/import-document.spec.ts @@ -57,16 +57,21 @@ test('story import: preview and import a Word manuscript', async ({ page }) => { title ); + // The settings page's Import button is the modal's second entry point; + // import-story.spec covers the library-menu one. Picking a file previews + // it at once. await gotoReady(page, `/universes/docland-${stamp}`); await page.getByRole('link', { name: 'Import and export' }).click(); - await page.locator('input[name="archive"]').setInputFiles({ + await page.getByRole('button', { name: 'Import a story...' }).click(); + const dialog = page.getByRole('dialog', { name: 'Import a story' }); + await expect(dialog).toBeVisible(); + await dialog.locator('input[name="archive"]').setInputFiles({ name: 'manuscript.docx', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', buffer: docx }); - await page.getByRole('button', { name: 'Preview' }).click(); - const report = page.locator('.import-report'); + const report = dialog.locator('.import-report'); await expect(report).toContainText(`"${title}": 2 chapters, 3 scenes`); await page.getByRole('button', { name: 'Import story' }).click(); diff --git a/e2e/import-story.spec.ts b/e2e/import-story.spec.ts index c5ad9a1..12c788f 100644 --- a/e2e/import-story.spec.ts +++ b/e2e/import-story.spec.ts @@ -3,7 +3,7 @@ import { gotoReady } from './navigate'; import { pickFromLibraryMenu, startStoryInUniverse } from './library'; // The story import round trip: write a story, download its export zip, and -// import the zip back through the universe settings preview flow. +// import the zip back through the library's import modal preview flow. test('story import: preview and import a story export zip', async ({ page }) => { await gotoReady(page, '/'); @@ -63,16 +63,23 @@ test('story import: preview and import a story export zip', async ({ page }) => expect(archive.status()).toBe(200); const zipBytes = await archive.body(); - await gotoReady(page, `/universes/importland-${stamp}`); - await page.getByRole('link', { name: 'Import and export' }).click(); - await page.locator('input[name="archive"]').setInputFiles({ + // Feed it back through the import modal, opened from the universe's New + // story menu on the library. Picking a file previews it at once. + await gotoReady(page, '/'); + await page + .locator('.universe-section', { hasText: universeName }) + .getByRole('button', { name: 'New story', exact: true }) + .click(); + await page.getByRole('menuitem', { name: 'Import a story into this universe...' }).click(); + const dialog = page.getByRole('dialog', { name: 'Import a story' }); + await expect(dialog).toBeVisible(); + await dialog.locator('input[name="archive"]').setInputFiles({ name: 'roundtrip.zip', mimeType: 'application/zip', buffer: zipBytes }); - await page.getByRole('button', { name: 'Preview' }).click(); - const report = page.locator('.import-report'); + const report = dialog.locator('.import-report'); await expect(report).toContainText('"Roundtrip": 1 chapter, 1 scene'); await expect(report).toContainText('A story named "Roundtrip" already exists'); await expect(report).toContainText('Bram'); diff --git a/src/lib/components/ImportStoryModal.svelte b/src/lib/components/ImportStoryModal.svelte new file mode 100644 index 0000000..dea7c77 --- /dev/null +++ b/src/lib/components/ImportStoryModal.svelte @@ -0,0 +1,354 @@ + + +{#if universe} +
+ {universe.standalone + ? 'The story comes in as a new standalone story.' + : `The story comes in as a new story in ${universe.name}.`} +
+{message}
+ {/if} + {#if busy} +{busyLabel}
+ {/if} + + {#if imported} ++ Imported {imported.sceneCount} + {imported.sceneCount === 1 ? 'scene' : 'scenes'}{imported.notesAttached > 0 + ? ` and ${imported.notesAttached} ${imported.notesAttached === 1 ? 'story note' : 'story notes'}` + : ''}{imported.entitiesCreated > 0 + ? `, creating ${imported.entitiesCreated} ${imported.entitiesCreated === 1 ? 'new entry' : 'new entries'}` + : ''}. +
+ {#each imported.problems as problem (problem)} +{problem}
+ {/each} ++ "{preview.storyTitle}": {preview.chapterCount} + {preview.chapterCount === 1 ? 'chapter' : 'chapters'}, + {preview.sceneCount} + {preview.sceneCount === 1 ? 'scene' : 'scenes'}, + {formatNumber(preview.words)} words{preview.assetCount > 0 + ? `, ${preview.assetCount} ${preview.assetCount === 1 ? 'image' : 'images'}` + : ''}. +
+ {#if preview.titleTaken} ++ A story named "{preview.storyTitle}" already exists here; this import creates a + second one. +
+ {/if} + {#if preview.assetCount > 0 && !preview.assetsConfigured} ++ Image storage is not configured, so the bundled images will not be imported. +
+ {/if} + {#if preview.notes.length > 0} +{problem}
+ {/each} +