Skip to content

Commit 9cd2c2e

Browse files
committed
Reduced duplicate members route tests
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release Kept route-specific members coverage and removed duplicate list, export, and import pass-through tests
1 parent dbcc942 commit 9cd2c2e

6 files changed

Lines changed: 3 additions & 290 deletions

File tree

apps/admin/src/layout/app-sidebar/member-sidebar-views.test.tsx

Lines changed: 0 additions & 67 deletions
This file was deleted.

apps/posts/test/unit/views/members/import-members/modal.test.tsx

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -107,55 +107,4 @@ describe('ImportMembersModal', () => {
107107

108108
expect(screen.getByRole('heading', {name: /import in progress/i})).toBeInTheDocument();
109109
});
110-
111-
it('passes import label metadata to onComplete for completed uploads', async () => {
112-
const onComplete = vi.fn();
113-
vi.stubGlobal('fetch', vi.fn(async () => new Response(JSON.stringify({
114-
meta: {
115-
stats: {imported: 1, invalid: []},
116-
import_label: {
117-
name: 'Import April',
118-
slug: 'import-april'
119-
}
120-
}
121-
}), {
122-
status: 200,
123-
headers: {'Content-Type': 'application/json'}
124-
})));
125-
126-
render(
127-
<ImportMembersModal
128-
open
129-
onComplete={onComplete}
130-
onOpenChange={() => {}}
131-
/>
132-
);
133-
134-
const dropTarget = screen.getByRole('button', {name: /select or drop a csv file/i});
135-
const csvFile = createFile('members.csv', 'text/csv', 'email,name\nmember@example.com,Member');
136-
137-
fireEvent.drop(dropTarget, {
138-
dataTransfer: {
139-
files: [csvFile],
140-
items: [{
141-
kind: 'file',
142-
type: csvFile.type,
143-
getAsFile: () => csvFile
144-
}],
145-
types: ['Files']
146-
}
147-
});
148-
149-
const importButton = await screen.findByRole('button', {name: /import 1 member/i});
150-
fireEvent.click(importButton);
151-
152-
await waitFor(() => {
153-
expect(onComplete).toHaveBeenCalledWith(expect.objectContaining({
154-
importLabel: {
155-
name: 'Import April',
156-
slug: 'import-april'
157-
}
158-
}));
159-
});
160-
});
161110
});

apps/posts/test/unit/views/members/members-actions.test.tsx

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,39 +52,6 @@ describe('MembersActions', () => {
5252
mockUseNavigate.mockReturnValue(vi.fn());
5353
});
5454

55-
it('passes onImportComplete to ImportMembersModal onComplete prop', () => {
56-
const onImportComplete = vi.fn();
57-
58-
render(
59-
<MembersActions
60-
hasFilterOrSearch={false}
61-
memberCount={10}
62-
search=""
63-
canBulkDelete
64-
onImportComplete={onImportComplete}
65-
/>
66-
);
67-
68-
expect(importModalPropsRef.current).not.toBeNull();
69-
const handleImportComplete = importModalPropsRef.current?.onComplete as ((result?: {importLabel?: {name: string; slug: string}}) => void) | undefined;
70-
71-
expect(handleImportComplete).toBeTypeOf('function');
72-
73-
handleImportComplete?.({
74-
importLabel: {
75-
name: 'Import April',
76-
slug: 'import-april'
77-
}
78-
});
79-
80-
expect(onImportComplete).toHaveBeenCalledWith({
81-
importLabel: {
82-
name: 'Import April',
83-
slug: 'import-april'
84-
}
85-
});
86-
});
87-
8855
it('opens the import modal when rendered on the import route', () => {
8956
mockUseLocation.mockReturnValue({
9057
pathname: '/members/import'

e2e/tests/admin/members/export.test.ts

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,6 @@ import {usePerTestIsolation} from '@/helpers/playwright/isolation';
55

66
usePerTestIsolation();
77

8-
const EXPECTED_CSV_HEADER_FIELDS = [
9-
'id,',
10-
'email,',
11-
'name,',
12-
'note,',
13-
'subscribed_to_emails,',
14-
'complimentary_plan,',
15-
'stripe_customer_id,',
16-
'created_at,',
17-
'deleted_at,',
18-
'labels,',
19-
'tiers'
20-
];
21-
228
test.describe('Ghost Admin - Members Export', () => {
239
test.use({labs: {membersForward: true}});
2410

@@ -34,27 +20,7 @@ test.describe('Ghost Admin - Members Export', () => {
3420
memberFactory = createMemberFactory(page.request);
3521
});
3622

37-
test('exports all members to a CSV with expected fields', async ({page}) => {
38-
await memberFactory.createMany(membersFixture);
39-
40-
const membersPage = new MembersListPage(page);
41-
await membersPage.goto();
42-
await membersPage.openActionsMenu();
43-
44-
const {suggestedFilename, content} = await membersPage.exportMembers();
45-
46-
expect(content).toMatch(new RegExp(EXPECTED_CSV_HEADER_FIELDS.join('')));
47-
48-
for (const member of membersFixture) {
49-
expect(content).toContain(member.name);
50-
expect(content).toContain(member.email);
51-
expect(content).toContain(member.note);
52-
}
53-
54-
expect(suggestedFilename).toMatch(/^members\.\d{4}-\d{2}-\d{2}\.csv$/);
55-
});
56-
57-
test('exports only filtered members when a filter is active', async ({page}) => {
23+
test('exports the filtered members from the React list route', async ({page}) => {
5824
await memberFactory.createMany(membersFixture);
5925

6026
const membersPage = new MembersListPage(page);

e2e/tests/admin/members/list.test.ts

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ test.describe('Ghost Admin - Members List', () => {
2020
await expect(page).toHaveURL(/\/ghost\/#\/members$/);
2121
});
2222

23-
test('displays members with name, email, status, and created date', async ({page}) => {
23+
test('renders the React members list on the members route', async ({page}) => {
2424
await memberFactory.createMany([
2525
{name: 'Alice Anderson', email: 'alice@example.com'},
2626
{name: 'Bob Baker', email: 'bob@example.com'},
@@ -34,31 +34,6 @@ test.describe('Ghost Admin - Members List', () => {
3434
await expect(membersPage.getMemberByName('Alice Anderson')).toBeVisible();
3535
await expect(membersPage.getMemberByName('Bob Baker')).toBeVisible();
3636
await expect(membersPage.getMemberByName('Charlie Clark')).toBeVisible();
37-
38-
// Each row shows the email and status
3937
await expect(membersPage.getMemberByName('Alice Anderson')).toContainText('alice@example.com');
40-
await expect(membersPage.getMemberByName('Alice Anderson')).toContainText('Free');
41-
});
42-
43-
test('shows empty state when there are no members', async ({page}) => {
44-
const membersPage = new MembersListPage(page);
45-
await membersPage.goto();
46-
47-
await expect(membersPage.emptyState).toBeVisible();
48-
await expect(membersPage.memberRows).toHaveCount(0);
49-
});
50-
51-
test('navigates to member detail when clicking a row', async ({page}) => {
52-
const member = await memberFactory.create({
53-
name: 'Detail Test Member',
54-
email: 'detail@example.com'
55-
});
56-
57-
const membersPage = new MembersListPage(page);
58-
await membersPage.goto();
59-
60-
await membersPage.openMemberByName('Detail Test Member');
61-
62-
await expect(page).toHaveURL(new RegExp(`/members/${member.id}`));
6338
});
6439
});

e2e/tests/admin/members/search-and-filter.test.ts

Lines changed: 1 addition & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,7 @@ test.describe('Ghost Admin - Members Search and Filter', () => {
1414
memberFactory = createMemberFactory(page.request);
1515
});
1616

17-
test('filters members by searching for a name and clears search to restore all', async ({page}) => {
18-
await memberFactory.createMany([
19-
{name: 'Unique Searchable Name', email: 'unique@example.com'},
20-
{name: 'Other Member', email: 'other@example.com'},
21-
{name: 'Another Member', email: 'another@example.com'}
22-
]);
23-
24-
const membersPage = new MembersListPage(page);
25-
await membersPage.goto();
26-
await expect(membersPage.memberRows).toHaveCount(3);
27-
28-
await membersPage.searchInput.fill('Unique Searchable');
29-
await expect(membersPage.memberRows).toHaveCount(1);
30-
await expect(membersPage.getMemberByName('Unique Searchable Name')).toBeVisible();
31-
32-
await membersPage.searchInput.clear();
33-
await expect(membersPage.memberRows).toHaveCount(3);
34-
});
35-
36-
test('filters members by label and updates the displayed count', async ({page}) => {
17+
test('applies an existing label filter on the members route', async ({page}) => {
3718
await memberFactory.createMany([
3819
{name: 'Labelled One', email: 'labelled1@example.com', labels: ['VIP']},
3920
{name: 'Labelled Two', email: 'labelled2@example.com', labels: ['VIP']},
@@ -49,62 +30,4 @@ test.describe('Ghost Admin - Members Search and Filter', () => {
4930
await expect(membersPage.getMemberByName('Labelled One')).toBeVisible();
5031
await expect(membersPage.getMemberByName('Labelled Two')).toBeVisible();
5132
});
52-
53-
test('combines multiple filters to narrow results and clears all at once', async ({page}) => {
54-
await memberFactory.createMany([
55-
{name: 'Alice Alpha', email: 'alice@alpha.com', labels: ['Premium']},
56-
{name: 'Alice Beta', email: 'alice@beta.com'},
57-
{name: 'Bob Alpha', email: 'bob@alpha.com', labels: ['Premium']},
58-
{name: 'Charlie Gamma', email: 'charlie@gamma.com'}
59-
]);
60-
61-
const membersPage = new MembersListPage(page);
62-
await membersPage.goto();
63-
await expect(membersPage.memberRows).toHaveCount(4);
64-
65-
await membersPage.addFilter('Name', 'Alice');
66-
await expect(membersPage.memberRows).toHaveCount(2);
67-
68-
await page.goto('/ghost/#/members?filter=name:~%27Alice%27%2Blabel:Premium');
69-
await expect(membersPage.memberRows).toHaveCount(1);
70-
await expect(membersPage.getMemberByName('Alice Alpha')).toBeVisible();
71-
72-
await membersPage.clearFiltersButton.click();
73-
await expect(membersPage.memberRows).toHaveCount(4);
74-
});
75-
76-
test('adds a second label filter without replacing the first', async ({page}) => {
77-
await memberFactory.createMany([
78-
{name: 'Both Labels', email: 'both@example.com', labels: ['VIP', 'Premium']},
79-
{name: 'VIP Only', email: 'vip@example.com', labels: ['VIP']},
80-
{name: 'Premium Only', email: 'premium@example.com', labels: ['Premium']},
81-
{name: 'No Label', email: 'nolabel@example.com'}
82-
]);
83-
84-
const membersPage = new MembersListPage(page);
85-
await membersPage.goto();
86-
await expect(membersPage.memberRows).toHaveCount(4);
87-
88-
await page.goto('/ghost/#/members?filter=label:VIP');
89-
await expect(membersPage.memberRows).toHaveCount(2);
90-
91-
await page.goto('/ghost/#/members?filter=label:VIP%2Blabel:Premium');
92-
await expect(membersPage.memberRows).toHaveCount(1);
93-
await expect(membersPage.getMemberByName('Both Labels')).toBeVisible();
94-
});
95-
96-
test('shows no results state when search matches nothing', async ({page}) => {
97-
await memberFactory.create({name: 'Existing Member', email: 'exists@example.com'});
98-
99-
const membersPage = new MembersListPage(page);
100-
await membersPage.goto();
101-
await expect(membersPage.memberRows).toHaveCount(1);
102-
103-
await membersPage.searchInput.fill('nonexistentnamestring');
104-
await expect(membersPage.noResults).toBeVisible();
105-
await expect(membersPage.showAllButton).toBeVisible();
106-
107-
await membersPage.searchInput.clear();
108-
await expect(membersPage.memberRows).toHaveCount(1);
109-
});
11033
});

0 commit comments

Comments
 (0)