-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathdatabase-csv-export.spec.ts
More file actions
191 lines (156 loc) · 6.29 KB
/
Copy pathdatabase-csv-export.spec.ts
File metadata and controls
191 lines (156 loc) · 6.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import { test, expect } from "./fixtures/auth";
import { createClient } from "@supabase/supabase-js";
// ---------------------------------------------------------------------------
// Admin client for cleanup
// ---------------------------------------------------------------------------
function getAdminClient() {
return createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SECRET_KEY!,
{ auth: { autoRefreshToken: false, persistSession: false } },
);
}
const createdDatabaseIds: string[] = [];
test.afterAll(async () => {
if (createdDatabaseIds.length === 0) return;
const admin = getAdminClient();
for (const id of createdDatabaseIds) {
await admin.from("pages").delete().eq("parent_id", id);
await admin.from("pages").delete().eq("id", id);
}
});
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
async function waitForSidebarTree(page: import("@playwright/test").Page) {
const sidebar = page.getByRole("complementary");
const treeLoaded = sidebar
.locator('[role="treeitem"], :text("No pages yet")')
.first();
await expect(treeLoaded).toBeVisible({ timeout: 15_000 });
return sidebar;
}
async function createDatabaseFromSidebar(
page: import("@playwright/test").Page,
): Promise<string> {
const sidebar = await waitForSidebarTree(page);
const newDbBtn = sidebar.getByTestId("sb-new-database-btn");
await expect(newDbBtn).toBeVisible({ timeout: 5_000 });
await newDbBtn.click();
await page.waitForURL(
(url) => url.pathname.split("/").filter(Boolean).length >= 2,
{ timeout: 15_000 },
);
const dbLoaded = page
.locator('[role="grid"], :text("No rows yet")')
.first();
await expect(dbLoaded).toBeVisible({ timeout: 15_000 });
const pathParts = new URL(page.url()).pathname.split("/").filter(Boolean);
const pageId = pathParts[pathParts.length - 1];
createdDatabaseIds.push(pageId);
return pageId;
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
test.describe("Database CSV Export", () => {
test("user can export a database as CSV via the toolbar button", async ({
authenticatedPage: page,
}) => {
await createDatabaseFromSidebar(page);
// Add a row so there's data to export
const addRowBtn = page.getByTestId("db-table-add-row");
await expect(addRowBtn).toBeVisible({ timeout: 10_000 });
await addRowBtn.click();
// Wait for the grid to appear
await expect(page.locator('[role="grid"]')).toBeVisible({
timeout: 10_000,
});
// Type a title for the first row
const firstTitleCell = page
.locator('[role="gridcell"]')
.first();
await expect(firstTitleCell).toBeVisible({ timeout: 5_000 });
await firstTitleCell.click();
await page.keyboard.type("Test Row Alpha");
await page.keyboard.press("Escape");
// Add a second row
await addRowBtn.click();
// Wait for the second row to appear
const gridCells = page.locator('[role="gridcell"]');
await expect(gridCells).toHaveCount(2, { timeout: 10_000 });
// Type a title for the second row
const secondTitleCell = gridCells.last();
await secondTitleCell.click();
await page.keyboard.type("Test Row Beta");
await page.keyboard.press("Escape");
// Wait for auto-save
await page.waitForTimeout(1000);
// Find and click the CSV export button
const exportBtn = page.getByTestId("csv-export-button");
await expect(exportBtn).toBeVisible({ timeout: 5_000 });
// Intercept the download
const downloadPromise = page.waitForEvent("download");
await exportBtn.click();
const download = await downloadPromise;
// Verify filename ends with .csv
expect(download.suggestedFilename()).toMatch(/\.csv$/);
// Read the downloaded content
const content = await (
await download.createReadStream()
)
.toArray()
.then((chunks) => Buffer.concat(chunks).toString("utf-8"));
// Verify header row contains "Title"
const lines = content.split("\r\n").filter((l) => l.length > 0);
expect(lines.length).toBeGreaterThanOrEqual(2);
expect(lines[0]).toContain("Title");
// Verify the row data is present
expect(content).toContain("Test Row Alpha");
expect(content).toContain("Test Row Beta");
});
test("CSV export button is visible in the database toolbar", async ({
authenticatedPage: page,
}) => {
await createDatabaseFromSidebar(page);
// Add a row to get the full toolbar
const addRowBtn = page.getByTestId("db-table-add-row");
await expect(addRowBtn).toBeVisible({ timeout: 10_000 });
await addRowBtn.click();
await expect(page.locator('[role="grid"]')).toBeVisible({
timeout: 10_000,
});
// The CSV export button should be visible
const exportBtn = page.getByTestId("csv-export-button");
await expect(exportBtn).toBeVisible({ timeout: 5_000 });
await expect(exportBtn).toContainText("Download CSV");
});
test("CSV export works on empty database", async ({
authenticatedPage: page,
}) => {
await createDatabaseFromSidebar(page);
// Even with no rows, the toolbar should show after the view loads.
// The "No rows yet" state still renders the view tabs and toolbar.
// Wait for the view to be loaded
const dbLoaded = page
.locator('[role="grid"], :text("No rows yet")')
.first();
await expect(dbLoaded).toBeVisible({ timeout: 15_000 });
// The export button may not be visible if the toolbar only renders
// when there's an active view. Let's check if it's there.
const exportBtn = page.getByTestId("csv-export-button");
// If the button is visible, clicking it should produce a CSV with just headers
if (await exportBtn.isVisible({ timeout: 3_000 }).catch(() => false)) {
const downloadPromise = page.waitForEvent("download");
await exportBtn.click();
const download = await downloadPromise;
const content = await (
await download.createReadStream()
)
.toArray()
.then((chunks) => Buffer.concat(chunks).toString("utf-8"));
// Should have at least the header row with "Title"
expect(content).toContain("Title");
}
});
});