From 74b2287d48b01a9729a6bdffa5492ef81a55da04 Mon Sep 17 00:00:00 2001 From: Jennie Robinson Faber Date: Tue, 14 Apr 2026 12:20:46 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20update=20tests=20+=20seed=20script,=20a?= =?UTF-8?q?dd=20ecology=E2=86=92board=20migration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useOnboarding.test.js: hasEngagedEcology→hasEngagedBoard, /api/ecology/suggestions→/api/board/suggestions, ecology key/route→board in test assertions - onboarding-status.test.js: stale description strings updated - seed-welcome-tester.cjs: communityEcology→board, ecologyPageVisited→boardPageVisited - migrate-ecology-to-board.cjs: one-time migration renames three member fields and activity log action values --- scripts/migrate-ecology-to-board.cjs | 69 +++++++++++++++++++ scripts/seed-welcome-tester.cjs | 6 +- .../client/composables/useOnboarding.test.js | 42 +++++------ tests/server/api/onboarding-status.test.js | 4 +- 4 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 scripts/migrate-ecology-to-board.cjs diff --git a/scripts/migrate-ecology-to-board.cjs b/scripts/migrate-ecology-to-board.cjs new file mode 100644 index 0000000..f8e84d9 --- /dev/null +++ b/scripts/migrate-ecology-to-board.cjs @@ -0,0 +1,69 @@ +/** + * One-time migration: rename ecology fields to board across members and activity logs. + * + * Renames on Member documents: + * communityEcology → board + * privacy.communityEcology → privacy.board + * onboarding.ecologyPageVisited → onboarding.boardPageVisited + * + * Renames on ActivityLog documents: + * action: 'community_ecology_updated' → action: 'board_updated' + * + * Usage: node scripts/migrate-ecology-to-board.cjs + */ +require('dotenv').config() +const mongoose = require('mongoose') + +async function main() { + const uri = process.env.MONGODB_URI + if (!uri) { + console.error('MONGODB_URI is not set. Aborting.') + process.exit(1) + } + + await mongoose.connect(uri) + console.log('Connected to MongoDB.') + + const Member = mongoose.connection.model( + 'Member', + new mongoose.Schema({}, { strict: false, collection: 'members' }), + ) + + const ActivityLog = mongoose.connection.model( + 'ActivityLog', + new mongoose.Schema({}, { strict: false, collection: 'activitylogs' }), + ) + + // --- Rename member fields --- + const memberResult = await Member.updateMany( + {}, + { + $rename: { + communityEcology: 'board', + 'privacy.communityEcology': 'privacy.board', + 'onboarding.ecologyPageVisited': 'onboarding.boardPageVisited', + }, + }, + { strict: false }, + ) + console.log( + `Members updated: ${memberResult.modifiedCount} modified (${memberResult.matchedCount} matched).`, + ) + + // --- Rename activity log action values --- + const logResult = await ActivityLog.updateMany( + { action: 'community_ecology_updated' }, + { $set: { action: 'board_updated' } }, + ) + console.log( + `Activity logs updated: ${logResult.modifiedCount} modified (${logResult.matchedCount} matched).`, + ) + + await mongoose.disconnect() + console.log('Done. Disconnected from MongoDB.') +} + +main().catch((err) => { + console.error(err) + process.exit(1) +}) diff --git a/scripts/seed-welcome-tester.cjs b/scripts/seed-welcome-tester.cjs index dc7918c..3d53bf2 100644 --- a/scripts/seed-welcome-tester.cjs +++ b/scripts/seed-welcome-tester.cjs @@ -1,6 +1,6 @@ /** * Seed a fresh member in the right state to test the Welcome Workflow. - * All onboarding flags default to false/null, no craft tags, no ecology topics. + * All onboarding flags default to false/null, no craft tags, no board topics. * * Usage: node scripts/seed-welcome-tester.js * Then pick "Welcome Tester" from the dev login dropdown. @@ -29,11 +29,11 @@ async function main() { role: "member", status: "active", craftTags: [], - communityEcology: { topics: [], offerPeerSupport: false }, + board: { topics: [], offerPeerSupport: false }, onboarding: { completedAt: null, eventPageVisited: false, - ecologyPageVisited: false, + boardPageVisited: false, wikiClicked: false, }, }, diff --git a/tests/client/composables/useOnboarding.test.js b/tests/client/composables/useOnboarding.test.js index b80610a..e48a481 100644 --- a/tests/client/composables/useOnboarding.test.js +++ b/tests/client/composables/useOnboarding.test.js @@ -40,7 +40,7 @@ describe('useOnboarding', () => { goals: { hasProfileTags: false, hasVisitedEvent: false, - hasEngagedEcology: false, + hasEngagedBoard: false, hasClickedWiki: false, }, completedAt: null, @@ -63,7 +63,7 @@ describe('useOnboarding', () => { goals: { hasProfileTags: true, hasVisitedEvent: false, - hasEngagedEcology: true, + hasEngagedBoard: true, hasClickedWiki: false, }, completedAt: null, @@ -81,7 +81,7 @@ describe('useOnboarding', () => { expect(goals.value.hasProfileTags).toBe(true) expect(goals.value.hasVisitedEvent).toBe(false) - expect(goals.value.hasEngagedEcology).toBe(true) + expect(goals.value.hasEngagedBoard).toBe(true) expect(goals.value.hasClickedWiki).toBe(false) }) @@ -93,14 +93,14 @@ describe('useOnboarding', () => { goals: { hasProfileTags: true, hasVisitedEvent: true, - hasEngagedEcology: true, + hasEngagedBoard: true, hasClickedWiki: true, }, completedAt: '2026-04-01T00:00:00Z', }) } if (url === '/api/events/recommended') return Promise.resolve([]) - if (url === '/api/ecology/suggestions') return Promise.resolve({ suggestions: [] }) + if (url === '/api/board/suggestions') return Promise.resolve({ suggestions: [] }) if (url === '/api/wiki/recommended') return Promise.resolve([]) return Promise.resolve(null) }) @@ -123,7 +123,7 @@ describe('useOnboarding', () => { goals: { hasProfileTags: true, hasVisitedEvent: true, - hasEngagedEcology: false, + hasEngagedBoard: false, hasClickedWiki: false, }, completedAt: null, @@ -162,7 +162,7 @@ describe('useOnboarding', () => { goals: { hasProfileTags: true, hasVisitedEvent: false, - hasEngagedEcology: false, + hasEngagedBoard: false, hasClickedWiki: false, }, completedAt: null, @@ -182,15 +182,15 @@ describe('useOnboarding', () => { expect(currentSuggestion.value.actionText).toBe('Browse events') }) - // 9.6: currentSuggestion returns ecology when tags + event done (priority 3) - it('9.6: currentSuggestion returns ecology when tags + event done', async () => { + // 9.6: currentSuggestion returns board when tags + event done (priority 3) + it('9.6: currentSuggestion returns board when tags + event done', async () => { fetchMock.mockImplementation((url) => { if (url === '/api/onboarding/status') { return Promise.resolve({ goals: { hasProfileTags: true, hasVisitedEvent: true, - hasEngagedEcology: false, + hasEngagedBoard: false, hasClickedWiki: false, }, completedAt: null, @@ -205,8 +205,8 @@ describe('useOnboarding', () => { expect(loading.value).toBe(false) }) - expect(currentSuggestion.value.key).toBe('ecology') - expect(currentSuggestion.value.action).toBe('/ecology') + expect(currentSuggestion.value.key).toBe('board') + expect(currentSuggestion.value.action).toBe('/board') }) // 9.7: currentSuggestion returns wiki when only wiki remaining (priority 4) @@ -217,7 +217,7 @@ describe('useOnboarding', () => { goals: { hasProfileTags: true, hasVisitedEvent: true, - hasEngagedEcology: true, + hasEngagedBoard: true, hasClickedWiki: false, }, completedAt: null, @@ -245,14 +245,14 @@ describe('useOnboarding', () => { goals: { hasProfileTags: true, hasVisitedEvent: true, - hasEngagedEcology: true, + hasEngagedBoard: true, hasClickedWiki: true, }, completedAt: '2026-04-01T00:00:00Z', }) } if (url === '/api/events/recommended') return Promise.resolve([]) - if (url === '/api/ecology/suggestions') return Promise.resolve({ suggestions: [] }) + if (url === '/api/board/suggestions') return Promise.resolve({ suggestions: [] }) if (url === '/api/wiki/recommended') return Promise.resolve([]) return Promise.resolve(null) }) @@ -280,7 +280,7 @@ describe('useOnboarding', () => { goals: { hasProfileTags: true, hasVisitedEvent: true, - hasEngagedEcology: true, + hasEngagedBoard: true, hasClickedWiki: true, }, completedAt: '2026-04-01T00:00:00Z', @@ -289,7 +289,7 @@ describe('useOnboarding', () => { if (url === '/api/events/recommended') { return Promise.resolve([{ _id: 'e1', title: 'Game Jam' }]) } - if (url === '/api/ecology/suggestions') { + if (url === '/api/board/suggestions') { return Promise.resolve({ suggestions: [{ name: 'Alex' }] }) } if (url === '/api/wiki/recommended') { @@ -320,7 +320,7 @@ describe('useOnboarding', () => { goals: { hasProfileTags: true, hasVisitedEvent: true, - hasEngagedEcology: true, + hasEngagedBoard: true, hasClickedWiki: true, }, completedAt: '2026-04-01T00:00:00Z', @@ -329,7 +329,7 @@ describe('useOnboarding', () => { if (url === '/api/events/recommended') { return Promise.resolve([{ _id: 'e1', title: 'Game Jam' }]) } - if (url === '/api/ecology/suggestions') { + if (url === '/api/board/suggestions') { return Promise.resolve({ suggestions: [] }) } if (url === '/api/wiki/recommended') { @@ -366,14 +366,14 @@ describe('useOnboarding', () => { goals: { hasProfileTags: true, hasVisitedEvent: true, - hasEngagedEcology: true, + hasEngagedBoard: true, hasClickedWiki: true, }, completedAt: '2026-04-01T00:00:00Z', }) } if (url === '/api/events/recommended') return Promise.resolve([]) - if (url === '/api/ecology/suggestions') return Promise.resolve({ suggestions: [] }) + if (url === '/api/board/suggestions') return Promise.resolve({ suggestions: [] }) if (url === '/api/wiki/recommended') return Promise.resolve([]) return Promise.resolve(null) }) diff --git a/tests/server/api/onboarding-status.test.js b/tests/server/api/onboarding-status.test.js index 1118329..d725f8f 100644 --- a/tests/server/api/onboarding-status.test.js +++ b/tests/server/api/onboarding-status.test.js @@ -46,7 +46,7 @@ describe('GET /api/onboarding/status', () => { }) // 1.2: hasProfileTags true when both tag types present - it('hasProfileTags is true when member has both craft tags and ecology topics', async () => { + it('hasProfileTags is true when member has both craft tags and board topics', async () => { requireAuth.mockResolvedValue({ _id: 'member-1', craftTags: ['game-design'], @@ -68,7 +68,7 @@ describe('GET /api/onboarding/status', () => { }) // 1.3: hasProfileTags false when only craft tags - it('hasProfileTags is false when member has craft tags but no ecology topics', async () => { + it('hasProfileTags is false when member has craft tags but no board topics', async () => { requireAuth.mockResolvedValue({ _id: 'member-1', craftTags: ['game-design'],