ui_migration feature(unitesting) modifying vitest config and spec, add test id flags to component

This commit is contained in:
Antonio Anaya 2026-05-14 22:07:38 -06:00
parent c7d16fa43f
commit 9968978a7b
3 changed files with 90 additions and 28 deletions

View file

@ -17,6 +17,7 @@
class="uk-button uk-button-default uk-width-1-1"
type="button"
@click="updateLogs()"
data-test-id="update-btn"
>
Refresh Logs
</button>
@ -27,6 +28,7 @@
:url="logFileURI"
button-label="Download Log File"
:button-primary="false"
data-test-id="download-btn"
/>
</div>
</div>

View file

@ -2,24 +2,40 @@ import { shallowMount, flushPromises } from "@vue/test-utils";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { createTestingPinia } from "@pinia/testing";
import axios from "axios";
import fs from "fs";
import path from "path";
import LoggingContent from "../../components/tabContentComponents/loggingContent.vue";
// Mock Axios
vi.mock("axios");
vi.mock("axios", () => {
return {
default: {
get: vi.fn(),
post: vi.fn(),
put: vi.fn(),
delete: vi.fn(),
patch: vi.fn(),
},
};
});
// Mock VueUse to prevent IntersectionObserver crashes in JSDOM
// This will be removed once vue-route is implemented
vi.mock("@vueuse/core", () => ({
useIntersectionObserver: vi.fn(() => ({
stop: vi.fn(),
})),
}));
describe("LoggingContent.vue", () => {
// Test Description
describe("Test LoggingContent.vue", () => {
let wrapper;
// A sample log string matching backend's format
const mockLogData = `[2026-05-14 08:46:12,808] [INFO] OFM server root logger has been set up at INFO level`;
// Define path to a real log file and read it
const logFilePath = path.resolve(__dirname, "../fixtures/realLog.txt");
const mockLogData = fs.readFileSync(logFilePath, "utf-8");
// Things to do before each test
beforeEach(async () => {
// Reset mocks before each test
vi.clearAllMocks();
@ -41,22 +57,32 @@ describe("LoggingContent.vue", () => {
],
// Simplify child components
stubs: {
PaginateLinks: true,
EndpointButton: true,
transition: false,
teleport: true,
},
PaginateLinks: true,
EndpointButton: true,
transition: true,
teleport: true,
settings: true,
},
},
});
await flushPromises();
// flush so we avoid leaving uncompleted processes running on background
await flushPromises();
});
// Render check
it("renders the component correctly", () => {
// Tear down wrapper, unmount testing component
afterEach(() => {
wrapper.unmount();
});
// Test 1: Render check, if things do exist in the page as intended
it("renders the component correctly", async () => {
// Check if component is been loaded and it is rendered
expect(wrapper.exists()).toBe(true);
// Check if navbar exists
expect(wrapper.find(".logging-navbar").exists()).toBe(true);
});
// Test 2: Check log parsing to see if specific INFO exists
it("fetches logs and parses them correctly when updateLogs is called", async () => {
// Trigger the method
await wrapper.vm.updateLogs();
@ -65,28 +91,60 @@ describe("LoggingContent.vue", () => {
// Verify Axios was called with the correct URI from the Pinia store
expect(axios.get).toHaveBeenCalledWith("http://microscope.local:5000/api/v3/log/");
// Verify the logs array was populated and reversed (newest first)
expect(wrapper.vm.logs.length).toBe(1);
// Verify the logs have correct file length
// Helps validate formatting, etc
expect(wrapper.vm.logs.length).toBe(5491);
// Check if the most recent log (ERROR) is first due to .reverse()
// Check the most recent log
expect(wrapper.vm.logs[0].level).toBe("INFO");
// Check if the multi-line traceback was appended correctly to the ERROR log
expect(wrapper.vm.logs[0].message).toContain("OFM");
expect(wrapper.vm.logs[0].summary).toContain("server");
// Check if the multi-line file has real first connection information
expect(wrapper.vm.logs[0].message).toContain("uvicorn");
expect(wrapper.vm.logs[0].summary).toContain("http://0.0.0.0:5000");
});
// Test 3: Filter logging file information
it("filters logs based on the selected level", async () => {
await wrapper.vm.updateLogs();
// Set filter to ERROR (should only show ERROR and CRITICAL)
wrapper.vm.filterLevel = "INFO";
// wait for Vue reactivity to update computed properties
// Set filter to WARNING wait and compare expected length
wrapper.vm.filterLevel = "WARNING";
await wrapper.vm.$nextTick();
expect(wrapper.vm.filteredItems.length).toBe(1449);
expect(wrapper.vm.filteredItems[0].level).toBe("WARNING");
// Only the 1 ERROR log should remain in filteredItems
expect(wrapper.vm.filteredItems.length).toBe(1);
// Set filter to INFO wait and compare expected length
wrapper.vm.filterLevel = "INFO";
await wrapper.vm.$nextTick();
expect(wrapper.vm.filteredItems.length).toBe(5491);
expect(wrapper.vm.filteredItems[0].level).toBe("INFO");
// Set filter to ERROR wait and compare expected length
wrapper.vm.filterLevel = "ERROR";
await wrapper.vm.$nextTick();
expect(wrapper.vm.filteredItems.length).toBe(7);
expect(wrapper.vm.filteredItems[0].level).toBe("ERROR");
});
// Test 4: Click buttons
// This required the component file to be modified by adding data-test-id flags.
// flags are needed as classes and text content may change by translation or style change.
it("click buttons", async () => {
// Click download button
const downloadButton = wrapper.find('[data-test-id="download-btn"]');
expect(downloadButton.exists()).toBe(true);
await downloadButton.trigger("click");
// Click update button
const updateButton = wrapper.find('[data-test-id="update-btn"]');
expect(updateButton.exists()).toBe(true);
await updateButton.trigger("click");
});
// Test 5: Compare downloaded file to fixture file.
// Test 6: WIP
afterAll(async () => {
await new Promise((resolve) => setTimeout(resolve, 15));
});
});

View file

@ -5,16 +5,18 @@ import { defineConfig } from "vitest/config";
import vue from "@vitejs/plugin-vue";
import path from "path";
console.log("\n\n::: Reading vitest config.")
console.log("\n\n::: Reading vitest config.");
export default defineConfig({
plugins: [vue({
plugins: [
vue({
template: {
compilerOptions: {
hoistStatic: false,
},
},
})],
}),
],
test: {
environment: "jsdom",
@ -31,7 +33,7 @@ export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"vue": path.resolve(__dirname, "./node_modules/vue"),
vue: path.resolve(__dirname, "./node_modules/vue"),
},
},
});