Guest
Finden Sie mit Vitest dynamisch gerenderte HTML-Objekte
Post
by Guest » 13 Jan 2025, 14:36
Ich schreibe gerade einen Unit-Test für eine Vue-Komponente. Die Komponente rendert den Grundriss eines Büros. Dazu wird ein SVG mit den Tabellen des Büros erstellt. Diese werden in einem JSON gespeichert.
Die Vue-Komponente (zu Ihrer Information: Tabelle bezieht sich auf einen Schreibtisch):
Code: Select all
import { onMounted, ref, watch } from 'vue';
import { json2SVG } from '@/converter/DbObjects2SvgParser';
import { useWallStore } from '@/stores/walls';
import { usePlaceStore } from '@/stores/tables';
import type { Wall } from '@/stores/walls';
import type { Place } from '@/stores/tables';
const wallsStore = useWallStore();
const wallsFromStore = ref([]);
const tablesStore = usePlaceStore();
const tablesFromStore = ref
([]);
let tables: HTMLCollectionOf = document.getElementsByClassName('main');
const selectedTable = ref(null);
const testSvg = ref();
const mapContainer = ref();
const imageHeight = ref(585);
const imageWidth = ref(1200);
const emit = defineEmits(['tableSelected']);
const props = defineProps(['placeIdFromButton']);
function placeClick(event: Event) {
const target = event.target as HTMLTableElement;
changeSelectedTable(target);
emit('tableSelected', target.id);
}
function changeSelectedTable(targetTable: HTMLTableElement) {
if (selectedTable.value) {
selectedTable.value.style.fill = ''; // Reset the previous selected table
}
targetTable.style.fill = 'white';
selectedTable.value = targetTable;
}
watch(props, () => {
console.log('watcher')
for (let i = 0; i < tables.length; i++) {
const table = tables[i];
if (table.id == props.placeIdFromButton) {
changeSelectedTable(table as HTMLTableElement);
}
}
});
onMounted(async () => {
await wallsStore.fetchWalls();
wallsFromStore.value = wallsStore.walls;
await tablesStore.fetchPlaces();
tablesFromStore.value = tablesStore.places;
/* parsing from json to DB */
// TODO should be moved into architect-app
//const parsingWalls = walls2DBParser(plan, "http://localhost:3000/walls")
//const parsingTables = tables2DBParser(plan, "http://localhost:3000/tables")
if (testSvg.value instanceof SVGElement) {
if (typeof document !== 'undefined'){
json2SVG(testSvg.value, wallsFromStore.value, tablesFromStore.value);
}
} else {
console.error('The element with ID "testSvg" is not an SVG element or does not exist.');
}
if (mapContainer.value) {
imageHeight.value = mapContainer.value.clientHeight - 10;
// imageWidth.value = mapContainer.clientWidth;
} else {
console.error('The element with ID "map-container" does not exist.');
}
tables = document.getElementsByClassName('table');
for (const table of tables) {
table.addEventListener('click', placeClick);
}
});
@use '@/assets/themes/itemisVariables';
.svgmap {
object-fit: cover;
width: 100%;
height: 100%;
}
.map-container {
overflow: auto;
border-style: solid;
border-color: itemisVariables.$itemisGrey;
}
.svg-field {
background-color: itemisVariables.$itemisGrey;
}
.testline {
stroke: yellow;
}
.wall {
stroke: itemisVariables.$itemisDarkBlue;
}
.table {
stroke: itemisVariables.$itemisDarkBlue;
fill: itemisVariables.$itemisBlue;
r: 5;
transition: fill 0.5s ease;
}
.table:hover {
fill: itemisVariables.$itemisGrey;
animation: pulse-color 1s infinite;
}
@keyframes pulse {
0% {
fill: itemisVariables.$itemisGrey;
}
50% {
fill: itemisVariables.$itemisBlue;
opacity: 0.7;
}
100% {
fill: itemisVariables.$itemisGrey;
}
}
Mein Test:
Code: Select all
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { flushPromises, mount, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import ImagePart03 from '@/views/NewReservation_03/ImagePart03.vue';
import { createPinia, setActivePinia } from 'pinia';
beforeEach(async () => {
setActivePinia(createPinia());
await nextTick();
})
describe('TagesberichteTag', () => {
const shallowMountCut = () => {
const cut = shallowMount(ImagePart03);
return {
findMapContainer: () => cut.find('.map-container'),
findFirstTable: () => cut.find('[id="1"]'),
findHeader: () => cut.find('.header'),
findPhrase: () => cut.find('.phrase')
};
};
it('renders map container', () => {
const { findMapContainer } = shallowMountCut();
expect(findMapContainer().exists()).toBe(true);
});
it('renders table', async () => {
await flushPromises()
await nextTick()
const { findFirstTable } = shallowMountCut();
await nextTick()
await flushPromises()
expect(findFirstTable().exists()).toBe(true);
});
});
Db2Objects2SvgParser.ts:
Code: Select all
import type { Wall } from '@/stores/walls';
import type { Place } from '@/stores/tables';
const SCALE = 10;
export const json2SVG = (svg: SVGElement, wallsFromDB: Wall[], tablesFromDB: Place[]): SVGElement => {
const svgWalls = wallObjectsIntoSVGElementSParser(wallsFromDB);
svg.append(svgWalls);
const svgTables = tableObjectsIntoSVGElementSParser(tablesFromDB);
svg.append(svgTables);
return svg;
};
export const wallObjectsIntoSVGElementSParser = (wallsFromDB: Wall[]) => {
const sgvWallsGroup: SVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'g');
sgvWallsGroup.setAttribute('id', 'walls');
for (const wall of wallsFromDB) {
const svgWall = svgWallFromDBWallcreate(wall);
sgvWallsGroup.appendChild(svgWall);
}
return sgvWallsGroup;
};
export const svgWallFromDBWallcreate = (wall: Wall) => {
const wallElement = document.createElementNS('http://www.w3.org/2000/svg', 'line');
wallElement.setAttribute('x1', (wall.coordsA.x * SCALE).toString());
wallElement.setAttribute('y1', (wall.coordsA.y * SCALE).toString());
wallElement.setAttribute('x2', (wall.coordsB.x * SCALE).toString());
wallElement.setAttribute('y2', (wall.coordsB.y * SCALE).toString());
wallElement.setAttribute('stroke', 'green');
wallElement.setAttribute('class', 'wall');
return wallElement;
};
export const tableObjectsIntoSVGElementSParser = (tablesFromDB: Place[]) => {
const svgTablesGroup: SVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'g');
svgTablesGroup.setAttribute('id', 'tables');
let tableIndex = 0;
for (const table of tablesFromDB) {
const svgTable = svgTableFromDBTableCreate(table);
tableIndex++;
const tableIndexString = tableIndex.toString();
svgTable.setAttribute('id', tableIndexString);
svgTablesGroup.appendChild(svgTable);
}
return svgTablesGroup;
};
export const svgTableFromDBTableCreate = (table: Place) => {
const tableElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
tableElement.setAttribute('r', '2');
tableElement.setAttribute('cx', (table.pos.x * SCALE).toString());
tableElement.setAttribute('cy', (table.pos.y * SCALE).toString());
tableElement.setAttribute('fill', 'red');
tableElement.setAttribute('stroke', 'green');
tableElement.setAttribute('stroke-width', '1');
tableElement.setAttribute('class', table.type);
//tableElement.setAttribute("onclick", "console.log($event.target)");
return tableElement;
};
Mit dem Debugger von intelliJ habe ich diesen Fehler gefunden und ich denke, das ist der Grund, warum ich die Tabelle mit ihrer ID in meinem Test nicht finden kann:
Code: Select all
ReferenceError: __vite_ssr_import_2__ is not defined
at eval (eval at json2SVG (/Users/userName/IdeaProjects/rare/Frontend/src/converter/DbObjects2SvgParser.ts:12:3), :1:1)
at Module.json2SVG (/Users/userName/IdeaProjects/rare/Frontend/src/converter/DbObjects2SvgParser.ts:12:3)
at /Users/userName/IdeaProjects/rare/Frontend/src/views/NewReservation_03/ImagePart03.vue:65:5
at processTicksAndRejections (node:internal/process/task_queues:95:5)
1736775370
Guest
Ich schreibe gerade einen Unit-Test für eine Vue-Komponente. Die Komponente rendert den Grundriss eines Büros. Dazu wird ein SVG mit den Tabellen des Büros erstellt. Diese werden in einem JSON gespeichert. Die Vue-Komponente (zu Ihrer Information: Tabelle bezieht sich auf einen Schreibtisch): [code]import { onMounted, ref, watch } from 'vue'; import { json2SVG } from '@/converter/DbObjects2SvgParser'; import { useWallStore } from '@/stores/walls'; import { usePlaceStore } from '@/stores/tables'; import type { Wall } from '@/stores/walls'; import type { Place } from '@/stores/tables'; const wallsStore = useWallStore(); const wallsFromStore = ref([]); const tablesStore = usePlaceStore(); const tablesFromStore = ref ([]); let tables: HTMLCollectionOf = document.getElementsByClassName('main'); const selectedTable = ref(null); const testSvg = ref(); const mapContainer = ref(); const imageHeight = ref(585); const imageWidth = ref(1200); const emit = defineEmits(['tableSelected']); const props = defineProps(['placeIdFromButton']); function placeClick(event: Event) { const target = event.target as HTMLTableElement; changeSelectedTable(target); emit('tableSelected', target.id); } function changeSelectedTable(targetTable: HTMLTableElement) { if (selectedTable.value) { selectedTable.value.style.fill = ''; // Reset the previous selected table } targetTable.style.fill = 'white'; selectedTable.value = targetTable; } watch(props, () => { console.log('watcher') for (let i = 0; i < tables.length; i++) { const table = tables[i]; if (table.id == props.placeIdFromButton) { changeSelectedTable(table as HTMLTableElement); } } }); onMounted(async () => { await wallsStore.fetchWalls(); wallsFromStore.value = wallsStore.walls; await tablesStore.fetchPlaces(); tablesFromStore.value = tablesStore.places; /* parsing from json to DB */ // TODO should be moved into architect-app //const parsingWalls = walls2DBParser(plan, "http://localhost:3000/walls") //const parsingTables = tables2DBParser(plan, "http://localhost:3000/tables") if (testSvg.value instanceof SVGElement) { if (typeof document !== 'undefined'){ json2SVG(testSvg.value, wallsFromStore.value, tablesFromStore.value); } } else { console.error('The element with ID "testSvg" is not an SVG element or does not exist.'); } if (mapContainer.value) { imageHeight.value = mapContainer.value.clientHeight - 10; // imageWidth.value = mapContainer.clientWidth; } else { console.error('The element with ID "map-container" does not exist.'); } tables = document.getElementsByClassName('table'); for (const table of tables) { table.addEventListener('click', placeClick); } }); @use '@/assets/themes/itemisVariables'; .svgmap { object-fit: cover; width: 100%; height: 100%; } .map-container { overflow: auto; border-style: solid; border-color: itemisVariables.$itemisGrey; } .svg-field { background-color: itemisVariables.$itemisGrey; } .testline { stroke: yellow; } .wall { stroke: itemisVariables.$itemisDarkBlue; } .table { stroke: itemisVariables.$itemisDarkBlue; fill: itemisVariables.$itemisBlue; r: 5; transition: fill 0.5s ease; } .table:hover { fill: itemisVariables.$itemisGrey; animation: pulse-color 1s infinite; } @keyframes pulse { 0% { fill: itemisVariables.$itemisGrey; } 50% { fill: itemisVariables.$itemisBlue; opacity: 0.7; } 100% { fill: itemisVariables.$itemisGrey; } } [/code] Mein Test: [code]import { describe, it, expect, beforeEach, vi } from 'vitest'; import { flushPromises, mount, shallowMount } from '@vue/test-utils'; import { nextTick } from 'vue'; import ImagePart03 from '@/views/NewReservation_03/ImagePart03.vue'; import { createPinia, setActivePinia } from 'pinia'; beforeEach(async () => { setActivePinia(createPinia()); await nextTick(); }) describe('TagesberichteTag', () => { const shallowMountCut = () => { const cut = shallowMount(ImagePart03); return { findMapContainer: () => cut.find('.map-container'), findFirstTable: () => cut.find('[id="1"]'), findHeader: () => cut.find('.header'), findPhrase: () => cut.find('.phrase') }; }; it('renders map container', () => { const { findMapContainer } = shallowMountCut(); expect(findMapContainer().exists()).toBe(true); }); it('renders table', async () => { await flushPromises() await nextTick() const { findFirstTable } = shallowMountCut(); await nextTick() await flushPromises() expect(findFirstTable().exists()).toBe(true); }); }); [/code] Db2Objects2SvgParser.ts: [code]import type { Wall } from '@/stores/walls'; import type { Place } from '@/stores/tables'; const SCALE = 10; export const json2SVG = (svg: SVGElement, wallsFromDB: Wall[], tablesFromDB: Place[]): SVGElement => { const svgWalls = wallObjectsIntoSVGElementSParser(wallsFromDB); svg.append(svgWalls); const svgTables = tableObjectsIntoSVGElementSParser(tablesFromDB); svg.append(svgTables); return svg; }; export const wallObjectsIntoSVGElementSParser = (wallsFromDB: Wall[]) => { const sgvWallsGroup: SVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'g'); sgvWallsGroup.setAttribute('id', 'walls'); for (const wall of wallsFromDB) { const svgWall = svgWallFromDBWallcreate(wall); sgvWallsGroup.appendChild(svgWall); } return sgvWallsGroup; }; export const svgWallFromDBWallcreate = (wall: Wall) => { const wallElement = document.createElementNS('http://www.w3.org/2000/svg', 'line'); wallElement.setAttribute('x1', (wall.coordsA.x * SCALE).toString()); wallElement.setAttribute('y1', (wall.coordsA.y * SCALE).toString()); wallElement.setAttribute('x2', (wall.coordsB.x * SCALE).toString()); wallElement.setAttribute('y2', (wall.coordsB.y * SCALE).toString()); wallElement.setAttribute('stroke', 'green'); wallElement.setAttribute('class', 'wall'); return wallElement; }; export const tableObjectsIntoSVGElementSParser = (tablesFromDB: Place[]) => { const svgTablesGroup: SVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'g'); svgTablesGroup.setAttribute('id', 'tables'); let tableIndex = 0; for (const table of tablesFromDB) { const svgTable = svgTableFromDBTableCreate(table); tableIndex++; const tableIndexString = tableIndex.toString(); svgTable.setAttribute('id', tableIndexString); svgTablesGroup.appendChild(svgTable); } return svgTablesGroup; }; export const svgTableFromDBTableCreate = (table: Place) => { const tableElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); tableElement.setAttribute('r', '2'); tableElement.setAttribute('cx', (table.pos.x * SCALE).toString()); tableElement.setAttribute('cy', (table.pos.y * SCALE).toString()); tableElement.setAttribute('fill', 'red'); tableElement.setAttribute('stroke', 'green'); tableElement.setAttribute('stroke-width', '1'); tableElement.setAttribute('class', table.type); //tableElement.setAttribute("onclick", "console.log($event.target)"); return tableElement; }; [/code] Mit dem Debugger von intelliJ habe ich diesen Fehler gefunden und ich denke, das ist der Grund, warum ich die Tabelle mit ihrer ID in meinem Test nicht finden kann: [code]ReferenceError: __vite_ssr_import_2__ is not defined at eval (eval at json2SVG (/Users/userName/IdeaProjects/rare/Frontend/src/converter/DbObjects2SvgParser.ts:12:3), :1:1) at Module.json2SVG (/Users/userName/IdeaProjects/rare/Frontend/src/converter/DbObjects2SvgParser.ts:12:3) at /Users/userName/IdeaProjects/rare/Frontend/src/views/NewReservation_03/ImagePart03.vue:65:5 at processTicksAndRejections (node:internal/process/task_queues:95:5) [/code]