Files
inkreach-official-website/apps/website/test/HomepageNavigationCarousel.test.ts
yeuimu 6c61a4e871 feat(deploy): production deployment setup and fixes
- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma
  binaryTargets for openssl 3.0
- nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal
  cron, http->https redirect
- prisma: add origin_goods.delisted migration, sync missing schema
  (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz
- api: CORS wildcard reflection, helmet CORP cross-origin, price
  backfill in persistProductDetail, categoryIcon ancestor fallback,
  mediaByColor per-color gallery in public goods detail
- admin: /admin base path (vite + router)
- import-data.mjs: udt_name casting, serial sequence advance fix
2026-08-26 14:23:09 +08:00

132 lines
6.0 KiB
TypeScript

import { mount } from '@vue/test-utils';
import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { ref } from 'vue';
import FeatureCards from '../app/components/FeatureCards.vue';
import StepProcess from '../app/components/StepProcess.vue';
const readComponent = (name: string) =>
readFileSync(resolve(process.cwd(), `app/components/${name}.vue`), 'utf8');
describe('homepage navigation and carousel behavior', () => {
afterEach(() => {
vi.useRealTimers();
vi.unstubAllGlobals();
});
it('uses current-page login navigation for every homepage CTA', () => {
const components = ['AppHeader', 'HeroBanner', 'StepProcess', 'CtaBanner'];
for (const component of components) {
const source = readComponent(component);
expect(source).not.toMatch(/target="_blank"[^>]*>\s*(登录|立即开始|免费试用|立即免费使用)/u);
expect(source).toContain("window.location.href = 'https://inkpod.vip/user/login'");
}
});
it('advances the four-step content automatically', async () => {
vi.useFakeTimers();
vi.stubGlobal('ref', ref);
const wrapper = mount(StepProcess);
expect(wrapper.get('[data-testid="step-image-0"]').classes()).toContain('opacity-100');
await vi.advanceTimersByTimeAsync(2000);
expect(wrapper.get('[data-testid="step-image-1"]').classes()).toContain('opacity-100');
wrapper.unmount();
});
it('fills the active step progress line before advancing', () => {
const source = readComponent('StepProcess');
expect(source).toContain('step-progress');
expect(source).toContain('animation: fill-step-progress 2s linear forwards');
expect(source).toContain('@keyframes fill-step-progress');
expect(source).toContain('to { width: 100%; }');
expect(source).toContain('class="step-title text-xl lg:text-4xl"');
expect(source).not.toContain('step-title text-xl font-bold');
});
it('advances the feature content every two seconds', async () => {
vi.useFakeTimers();
vi.stubGlobal('ref', ref);
const wrapper = mount(FeatureCards);
expect(wrapper.get('[data-testid="feature-image-0"]').classes()).toContain('opacity-100');
await vi.advanceTimersByTimeAsync(2000);
expect(wrapper.get('[data-testid="feature-image-1"]').classes()).toContain('opacity-100');
wrapper.unmount();
});
it('keeps feature items aligned with the image and uses restrained titles', () => {
const source = readComponent('FeatureCards');
expect(source).toContain('feature-list');
expect(source).toContain('feature-item-title text-xl lg:text-3xl');
expect(source).toContain('grid-template-rows: repeat(4, minmax(0, 1fr))');
});
it('runs the customer-case track as an automatic marquee', () => {
const source = readComponent('CustomerCases');
expect(source).toContain('animation: case-marquee 30s');
expect(source).toContain('@media (prefers-reduced-motion: reduce)');
});
it('keeps the hero visual as a capped image instead of a background', () => {
const source = readComponent('HeroBanner');
expect(source).toMatch(/<img[^>]+src="\/主视觉\.png"/u);
expect(source).not.toMatch(/background(?:-image)?:\s*[^;]*主视觉\.png/u);
expect(source).toContain('hero-visual');
});
it('styles the hero emphasis and separates the three selling points', () => {
const source = readComponent('HeroBanner');
expect(source).toContain('hero-copy');
expect(source).toContain('hero-gradient-text');
expect(source).toContain('linear-gradient(90deg, #111827 0%, #111827 28%, #ff6800 100%)');
expect(source).toContain('.hero-copy { align-items: flex-start; }');
expect(source).toContain('hero-subtitle font-thin');
expect(source).toContain('selling-point-separator');
expect(source).toContain('aria-hidden="true">|</span>');
});
it('loads the supplied PingFang SC font weights globally', () => {
const css = readFileSync(resolve(process.cwd(), 'app/assets/css/tailwind.css'), 'utf8');
expect(css).toContain("font-family: 'PingFang SC'");
expect(css).toContain("url('/fonts/pingfang-sc-regular.ttf')");
expect(css).toContain("url('/fonts/pingfang-sc-thin.ttf')");
expect(css).toContain("url('/fonts/pingfang-sc-medium.ttf')");
expect(css).toContain("url('/fonts/pingfang-sc-semibold.ttf')");
expect(existsSync(resolve(process.cwd(), 'public/fonts/pingfang-sc-regular.ttf'))).toBe(true);
expect(existsSync(resolve(process.cwd(), 'public/fonts/pingfang-sc-thin.ttf'))).toBe(true);
expect(existsSync(resolve(process.cwd(), 'public/fonts/pingfang-sc-medium.ttf'))).toBe(true);
expect(existsSync(resolve(process.cwd(), 'public/fonts/pingfang-sc-semibold.ttf'))).toBe(true);
});
it('uses the theme color for trust statistics', () => {
const source = readComponent('TrustSection');
expect(source).toContain('trust-stat-value text-primary');
expect(source).toContain('trust-illustration');
expect(source).toContain('margin-bottom: -40px');
});
it('temporarily hides recommendation and solution navigation', () => {
const source = readComponent('AppHeader');
expect(source).toContain('v-if="SHOW_EXTENDED_NAV"');
});
it('uses a compact desktop navigation font size', () => {
const source = readComponent('AppHeader');
expect(source).toContain('desktop-nav');
expect(source).toContain('.desktop-nav { font-size: 18px; }');
});
it('aligns country flags and keeps the more-products action at the bottom', () => {
const source = readComponent('PodProducts');
expect(source).toContain('country-button');
expect(source).toContain('country-flag');
expect(source).toContain('more-products');
expect(source).toContain('more-products-link text-primary');
expect(source).not.toContain('fa-arrow-right');
expect(source).toContain('margin-top: auto');
});
});