290 lines
11 KiB
JavaScript
290 lines
11 KiB
JavaScript
import { afterEach, beforeEach, describe, expect, it, jest } from "@jest/globals";
|
|
import { RouterLinkStub, flushPromises, mount, shallowMount } from "@vue/test-utils";
|
|
|
|
import PlatformSection from "../../../frontend/src/components/PlatformSection.vue";
|
|
import { auth } from "../../../frontend/src/auth.js";
|
|
import { homepageContentForMode, normalizeHomepageFocus } from "../../../frontend/src/data/homepageContent.js";
|
|
import HomeView from "../../../frontend/src/views/HomeView.vue";
|
|
|
|
let mockRoute = { meta: { homepageFocus: "sdet" }, params: {}, query: {} };
|
|
|
|
jest.mock("vue-router", () => ({
|
|
RouterLink: {
|
|
name: "RouterLink",
|
|
props: ["to"],
|
|
template: "<a :href=\"typeof to === 'string' ? to : '#'\" @click=\"$emit('click', $event)\"><slot /></a>",
|
|
},
|
|
useRoute: () => mockRoute,
|
|
}), { virtual: true });
|
|
|
|
function resetAuth() {
|
|
auth.ready = true;
|
|
auth.enabled = true;
|
|
auth.authenticated = false;
|
|
auth.username = "";
|
|
auth.email = "";
|
|
auth.emailVerified = null;
|
|
auth.groups = [];
|
|
auth.resetUrl = "https://sso.example.dev/reset";
|
|
}
|
|
|
|
describe("HomeView", () => {
|
|
beforeEach(() => {
|
|
mockRoute = { meta: { homepageFocus: "sdet" }, params: {}, query: {} };
|
|
global.fetch = jest.fn(async (resource) => {
|
|
const url = typeof resource === "string" ? resource : resource?.url || "";
|
|
if (url.includes("/api/account/member-state")) {
|
|
return new Response(
|
|
JSON.stringify({
|
|
status: "ready",
|
|
onboarding_url: "/onboarding?code=ada~CODE",
|
|
dashboard_url: "/apps",
|
|
account_url: "/account",
|
|
}),
|
|
{ status: 200, headers: { "content-type": "application/json" } },
|
|
);
|
|
}
|
|
return new Response("{}", { status: 200, headers: { "content-type": "application/json" } });
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
resetAuth();
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
it("renders the professional homepage while logged out", () => {
|
|
resetAuth();
|
|
const wrapper = mount(HomeView, {
|
|
global: {
|
|
stubs: {
|
|
RouterLink: RouterLinkStub,
|
|
PlatformSection: true,
|
|
},
|
|
},
|
|
props: {
|
|
labStatus: {
|
|
connected: true,
|
|
atlas: { up: true, known: true },
|
|
active_service: { known: true, label: "Nextcloud", url: "https://cloud.example.dev", window: "15m" },
|
|
dedicated_hosts: {
|
|
known: true,
|
|
up: true,
|
|
up_count: 2,
|
|
total: 2,
|
|
hosts: [
|
|
{ label: "Database node", known: true, up: true },
|
|
{ label: "Jumphost", known: true, up: true },
|
|
],
|
|
},
|
|
checked_at: 1000,
|
|
},
|
|
loading: false,
|
|
error: "",
|
|
},
|
|
});
|
|
|
|
expect(wrapper.text()).toContain("Brad Stein");
|
|
expect(wrapper.text()).toContain("Senior SDET / DevOps Automation Engineer");
|
|
expect(wrapper.text()).toContain("Discuss a Project");
|
|
expect(wrapper.text()).toContain("View Résumé");
|
|
expect(wrapper.text()).toContain("Explore the Live Platform");
|
|
expect(wrapper.text()).toContain("Quality Systems I Can Take From Zero to Reliable");
|
|
expect(wrapper.text()).toContain("Testing, Automation, and Platform Work");
|
|
expect(wrapper.text()).toContain("Atlas operator tools");
|
|
expect(wrapper.text()).toContain("Cassandra");
|
|
expect(wrapper.text()).not.toContain("TaskWatcher");
|
|
expect(wrapper.text()).toContain("family, friends, and the lucky few");
|
|
expect(wrapper.text()).toContain("Register");
|
|
expect(wrapper.text()).toContain("Request Lab Access");
|
|
expect(wrapper.find("[aria-label='Keycloak: Use one account to sign in across bstein.dev.']").exists()).toBe(true);
|
|
expect(wrapper.find("[title='Nextcloud: Keep files, photos, office docs, and personal cloud data in sync.']").exists()).toBe(true);
|
|
expect(wrapper.find("a[href='https://bstein.dev/ai/chat']").exists()).toBe(true);
|
|
expect(wrapper.find("a[href='https://bstein.dev/monero']").exists()).toBe(true);
|
|
expect(wrapper.find("a[href='https://chat.ai.bstein.dev']").exists()).toBe(false);
|
|
expect(wrapper.find("a[href='https://monero.bstein.dev:443']").exists()).toBe(false);
|
|
expect(wrapper.find("a[href='/ai/chat']").exists()).toBe(false);
|
|
expect(wrapper.find("a[href='/monero']").exists()).toBe(false);
|
|
expect(wrapper.text()).not.toContain("Existing registration, verification, onboarding");
|
|
expect(wrapper.text()).not.toContain("Looking for hosted services or an existing Titan Lab account");
|
|
expect(wrapper.find("a[href^='mailto:brad@bstein.dev']").exists()).toBe(true);
|
|
expect(wrapper.findComponent(PlatformSection).exists()).toBe(true);
|
|
expect(wrapper.text()).toContain("Atlas cluster");
|
|
expect(wrapper.text()).toContain("Most requested service");
|
|
expect(wrapper.text()).toContain("Nextcloud");
|
|
expect(wrapper.text()).toContain("Outpost hosts");
|
|
expect(wrapper.text()).toContain("2 of 2 online");
|
|
expect(wrapper.text()).toContain("Database machine");
|
|
expect(wrapper.text()).toContain("Jumphost");
|
|
expect(wrapper.text()).toContain("Responding");
|
|
});
|
|
|
|
it("shows a calm fallback when public status is unavailable", () => {
|
|
resetAuth();
|
|
const wrapper = mount(HomeView, {
|
|
global: {
|
|
stubs: {
|
|
RouterLink: RouterLinkStub,
|
|
PlatformSection: true,
|
|
},
|
|
},
|
|
props: {
|
|
labStatus: null,
|
|
loading: false,
|
|
error: "Live data unavailable",
|
|
},
|
|
});
|
|
|
|
expect(wrapper.text()).toContain("Live status is temporarily unavailable");
|
|
expect(wrapper.text()).not.toContain("status 500");
|
|
});
|
|
|
|
it("shows authenticated member actions without exposing service secrets", async () => {
|
|
resetAuth();
|
|
auth.authenticated = true;
|
|
auth.username = "ada";
|
|
auth.email = "ada@example.dev";
|
|
|
|
const wrapper = mount(HomeView, {
|
|
global: {
|
|
stubs: {
|
|
RouterLink: RouterLinkStub,
|
|
PlatformSection: true,
|
|
},
|
|
},
|
|
props: {
|
|
labStatus: { connected: true, atlas: { up: true, known: true } },
|
|
loading: false,
|
|
error: "",
|
|
},
|
|
});
|
|
await flushPromises();
|
|
|
|
expect(wrapper.text()).toContain("Open Services");
|
|
expect(wrapper.text()).toContain("Account");
|
|
expect(wrapper.text()).not.toContain("app_password");
|
|
expect(wrapper.text()).not.toContain("mailu_app_password");
|
|
});
|
|
|
|
it("builds developer and SDET homepage modes", () => {
|
|
const devops = homepageContentForMode("devops");
|
|
const developer = homepageContentForMode("developer");
|
|
const sdet = homepageContentForMode("sdet");
|
|
|
|
expect(normalizeHomepageFocus("dev")).toBe("developer");
|
|
expect(devops.summary).toContain("custom software, tooling, and infrastructure");
|
|
expect(devops.techStack).toEqual([
|
|
"Kubernetes",
|
|
"Docker",
|
|
"Linux",
|
|
"Terraform",
|
|
"Ansible",
|
|
"Flux",
|
|
"Helm",
|
|
"Jenkins",
|
|
"Harbor",
|
|
"Keycloak",
|
|
"Vault",
|
|
"Grafana",
|
|
"Prometheus",
|
|
"Python",
|
|
"Bash",
|
|
"Pytest",
|
|
"Selenium",
|
|
"Playwright",
|
|
]);
|
|
expect(developer.focus).toBe("developer");
|
|
expect(developer.role).toBe("Full-Stack Platform Engineer / Senior SDET");
|
|
expect(developer.resumeUrl).toBe("/developer/about");
|
|
expect(developer.techStack).toContain("Vue");
|
|
expect(developer.techStack.indexOf("Selenium")).toBeLessThan(developer.techStack.indexOf("Playwright"));
|
|
expect(developer.workSection.heading).toBe("Product and Platform Showcase");
|
|
expect(developer.selectedWork[0].title).toBe("Cassandra");
|
|
|
|
expect(sdet.focus).toBe("sdet");
|
|
expect(sdet.role).toBe("Senior SDET / DevOps Automation Engineer");
|
|
expect(sdet.resumeUrl).toBe("/about");
|
|
expect(sdet.techStack).toContain("Selenium");
|
|
expect(sdet.techStack.indexOf("Selenium")).toBeLessThan(sdet.techStack.indexOf("Playwright"));
|
|
expect(sdet.capabilities[0].title).toBe("Test Automation & SDET");
|
|
expect(sdet.selectedWork[0].title).toBe("Boeing");
|
|
});
|
|
});
|
|
|
|
describe("PlatformSection", () => {
|
|
it("preserves platform service, status, metric, and diagram behavior", () => {
|
|
const wrapper = shallowMount(PlatformSection, {
|
|
global: {
|
|
stubs: {
|
|
MetricRow: true,
|
|
ServiceGrid: true,
|
|
MermaidCard: true,
|
|
},
|
|
},
|
|
props: {
|
|
labStatus: {
|
|
connected: true,
|
|
atlas: { up: true, known: true },
|
|
dedicated_hosts: {
|
|
known: true,
|
|
up: false,
|
|
up_count: 1,
|
|
total: 2,
|
|
hosts: [
|
|
{ label: "Database node", known: true, up: true },
|
|
{ label: "Jumphost", known: true, up: false },
|
|
],
|
|
},
|
|
},
|
|
serviceData: {
|
|
services: [
|
|
{ name: "Nextcloud", summary: "Storage", link: "https://cloud.example.dev" },
|
|
{ name: "Monero", summary: "Node", link: "/monero", host: "monerod.crypto.svc.cluster.local:18081" },
|
|
{ name: "Mystery", summary: "Default", link: "https://default.example.dev" },
|
|
],
|
|
},
|
|
loading: true,
|
|
error: "",
|
|
},
|
|
});
|
|
|
|
expect(wrapper.vm.atlasPillClass).toBe("pill-ok");
|
|
expect(wrapper.vm.dedicatedHostsPillClass).toBe("pill-bad");
|
|
expect(wrapper.vm.dedicatedHostsLabel).toBe("1/2 responding");
|
|
expect(wrapper.vm.dedicatedHostItems.map((host) => host.value)).toEqual(["Responding", "Needs attention"]);
|
|
expect(wrapper.get(".status").text()).toBe("Loading...");
|
|
expect(wrapper.vm.displayServices.map((service) => service.icon)).toEqual(["NC", "XM", "TL"]);
|
|
expect(wrapper.vm.displayServices.find((service) => service.name === "Monero").host).toBe("/monero");
|
|
expect(wrapper.vm.hardwareDiagram).toContain("Titan Lab");
|
|
expect(wrapper.vm.hardwareDiagram).toContain("Dedicated non-cluster hosts");
|
|
expect(wrapper.vm.networkDiagram).toContain("oauth2-proxy");
|
|
expect(wrapper.vm.pipelineDiagram).toContain("Flux");
|
|
expect(wrapper.text()).toContain("IaC source");
|
|
expect(wrapper.text()).toContain("Titan Lab: Live Platform");
|
|
});
|
|
|
|
it("shows public status errors without raw API detail", () => {
|
|
const wrapper = shallowMount(PlatformSection, {
|
|
global: {
|
|
stubs: {
|
|
MetricRow: true,
|
|
ServiceGrid: true,
|
|
MermaidCard: true,
|
|
},
|
|
},
|
|
props: {
|
|
labStatus: { connected: false, atlas: { up: false, known: false } },
|
|
serviceData: { services: [] },
|
|
metricsData: { items: [{ label: "Custom", value: "1", note: "" }] },
|
|
loading: false,
|
|
error: "Live data unavailable",
|
|
},
|
|
});
|
|
|
|
expect(wrapper.vm.atlasPillClass).toBe("pill-bad");
|
|
expect(wrapper.get(".status").text()).toBe("Live data unavailable");
|
|
expect(wrapper.vm.metricItems[0].note).toBe("");
|
|
expect(wrapper.text()).toContain("The platform overview remains available below");
|
|
});
|
|
});
|