wekan/tests/wekanCreator.import.test.js
2025-11-11 17:29:40 +02:00

313 lines
8 KiB
JavaScript

/**
* Test: WekanCreator import with swimlane preservation
*
* Simulates exporting a board with swimlanes and importing it back,
* verifying that:
* 1. Swimlanes are correctly mapped from old IDs to new IDs
* 2. Cards reference the correct swimlane IDs after import
* 3. A default swimlane is created when no swimlanes exist
* 4. ID normalization (id → _id) works for all exported items
*/
// Mock data: exported board with swimlanes and cards
const mockExportedBoard = {
_format: 'wekan-board-1.0.0',
_id: 'board1',
title: 'Test Board',
archived: false,
color: 'bgnone',
permission: 'private',
createdAt: new Date().toISOString(),
modifiedAt: new Date().toISOString(),
members: [
{
userId: 'user1',
wekanId: 'user1',
isActive: true,
isAdmin: true,
},
],
labels: [],
swimlanes: [
{
_id: 'swimlane1',
title: 'Swimlane 1',
archived: false,
sort: 0,
},
{
_id: 'swimlane2',
title: 'Swimlane 2',
archived: false,
sort: 1,
},
],
lists: [
{
_id: 'list1',
title: 'To Do',
archived: false,
sort: 0,
},
{
_id: 'list2',
title: 'Done',
archived: false,
sort: 1,
},
],
cards: [
{
_id: 'card1',
title: 'Card in swimlane 1',
archived: false,
swimlaneId: 'swimlane1',
listId: 'list1',
sort: 0,
description: 'Test card',
dateLastActivity: new Date().toISOString(),
labelIds: [],
},
{
_id: 'card2',
title: 'Card in swimlane 2',
archived: false,
swimlaneId: 'swimlane2',
listId: 'list2',
sort: 0,
description: 'Another test card',
dateLastActivity: new Date().toISOString(),
labelIds: [],
},
],
comments: [],
activities: [
{
activityType: 'createBoard',
createdAt: new Date().toISOString(),
userId: 'user1',
},
],
checklists: [],
checklistItems: [],
subtaskItems: [],
customFields: [],
rules: [],
triggers: [],
actions: [],
users: [
{
_id: 'user1',
username: 'admin',
profile: {
fullname: 'Admin User',
},
},
],
};
// Export format variation: using 'id' instead of '_id'
const mockExportedBoardWithIdField = {
...mockExportedBoard,
swimlanes: [
{
id: 'swimlane1',
title: 'Swimlane 1 (id variant)',
archived: false,
sort: 0,
},
],
lists: [
{
id: 'list1',
title: 'To Do (id variant)',
archived: false,
sort: 0,
},
],
cards: [
{
id: 'card1',
title: 'Card (id variant)',
archived: false,
swimlaneId: 'swimlane1',
listId: 'list1',
sort: 0,
description: 'Test card with id field',
dateLastActivity: new Date().toISOString(),
labelIds: [],
},
],
};
// Test: Verify id → _id normalization
function testIdNormalization() {
console.log('\n=== Test: ID Normalization (id → _id) ===');
// Simulate the normalization logic from WekanCreator constructor
const normalizeIds = arr => {
if (!arr) return;
arr.forEach(item => {
if (item && item.id && !item._id) {
item._id = item.id;
}
});
};
const testData = {
lists: mockExportedBoardWithIdField.lists,
cards: mockExportedBoardWithIdField.cards,
swimlanes: mockExportedBoardWithIdField.swimlanes,
};
normalizeIds(testData.lists);
normalizeIds(testData.cards);
normalizeIds(testData.swimlanes);
// Check results
if (testData.swimlanes[0]._id === 'swimlane1') {
console.log('✓ Swimlane: id → _id normalization created _id');
} else {
console.log('✗ Swimlane: id → _id normalization FAILED');
}
if (testData.lists[0]._id === 'list1') {
console.log('✓ List: id → _id normalization created _id');
} else {
console.log('✗ List: id → _id normalization FAILED');
}
if (testData.cards[0]._id === 'card1') {
console.log('✓ Card: id → _id normalization created _id');
} else {
console.log('✗ Card: id → _id normalization FAILED');
}
}
// Test: Verify swimlane mapping during import
function testSwimlaneMapping() {
console.log('\n=== Test: Swimlane Mapping (export → import) ===');
// Simulate WekanCreator swimlane mapping
const swimlanes = {};
const swimlaneIndexMap = {}; // Track old → new ID mappings
// Simulate createSwimlanes: build mapping of old ID → new ID
mockExportedBoard.swimlanes.forEach((swimlane, index) => {
const oldId = swimlane._id;
const newId = `new_swimlane_${index}`; // Simulated new ID
swimlanes[oldId] = newId;
swimlaneIndexMap[oldId] = newId;
});
console.log(`✓ Created mapping for ${Object.keys(swimlanes).length} swimlanes:`);
Object.entries(swimlanes).forEach(([oldId, newId]) => {
console.log(` ${oldId}${newId}`);
});
// Simulate createCards: cards reference swimlanes using mapping
const cardSwimlaneCheck = mockExportedBoard.cards.every(card => {
const oldSwimlaneId = card.swimlaneId;
const newSwimlaneId = swimlanes[oldSwimlaneId];
return newSwimlaneId !== undefined;
});
if (cardSwimlaneCheck) {
console.log('✓ All cards can be mapped to swimlanes');
} else {
console.log('✗ Some cards have missing swimlane mappings');
}
}
// Test: Verify default swimlane creation when none exist
function testDefaultSwimlaneCreation() {
console.log('\n=== Test: Default Swimlane Creation ===');
const boardWithoutSwimlanes = {
...mockExportedBoard,
swimlanes: [],
};
// Simulate the default swimlane logic from WekanCreator
let swimlanes = {};
let defaultSwimlaneId = null;
// If no swimlanes were provided, create a default
if (!swimlanes || Object.keys(swimlanes).length === 0) {
defaultSwimlaneId = 'new_default_swimlane';
console.log('✓ Default swimlane created:', defaultSwimlaneId);
}
// Verify cards without swimlane references use the default
const cardsWithoutSwimlane = boardWithoutSwimlanes.cards.filter(c => !c.swimlaneId);
if (cardsWithoutSwimlane.length > 0 && defaultSwimlaneId) {
console.log(`${cardsWithoutSwimlane.length} cards will use default swimlane`);
} else if (cardsWithoutSwimlane.length === 0) {
console.log('✓ No cards lacking swimlane (test data all have swimlaneId)');
}
}
// Test: Verify swimlane + card integrity after full import cycle
function testFullImportCycle() {
console.log('\n=== Test: Full Import Cycle ===');
// Step 1: Normalize IDs
const normalizeIds = arr => {
if (!arr) return;
arr.forEach(item => {
if (item && item.id && !item._id) {
item._id = item.id;
}
});
};
const data = JSON.parse(JSON.stringify(mockExportedBoard)); // Deep copy
normalizeIds(data.swimlanes);
normalizeIds(data.lists);
normalizeIds(data.cards);
// Step 2: Map swimlanes
const swimlaneMap = {};
data.swimlanes.forEach((s, idx) => {
swimlaneMap[s._id] = `imported_swimlane_${idx}`;
});
// Step 3: Verify cards get mapped swimlane IDs
let unmappedCards = 0;
data.cards.forEach(card => {
if (card.swimlaneId && !swimlaneMap[card.swimlaneId]) {
unmappedCards++;
}
});
if (unmappedCards === 0) {
console.log('✓ All cards have valid swimlane references');
} else {
console.log(`${unmappedCards} cards have unmapped swimlane references`);
}
if (data.swimlanes.length > 0) {
console.log(`${data.swimlanes.length} swimlanes preserved in import`);
}
if (data.cards.length > 0) {
console.log(`${data.cards.length} cards preserved in import`);
}
}
// Run all tests
if (typeof describe === 'undefined') {
// Running in Node.js or standalone (not Mocha)
console.log('====================================');
console.log('WekanCreator Import Tests');
console.log('====================================');
testIdNormalization();
testSwimlaneMapping();
testDefaultSwimlaneCreation();
testFullImportCycle();
console.log('\n====================================');
console.log('Tests completed');
console.log('====================================\n');
}