/* eslint-env jest */ import { act } from 'react'; import { createRoot, type Root } from 'react-dom/client'; import { MemoryRouter } from 'react-router-dom'; import SeoSetup from '../SeoSetup'; import { fetchSeoFiles, fetchSeoImportSources, fetchSeoSettings, runSeoImportBatch, switchSeoPlugin, updateSeoSettings, } from '../../../service/seo/seo.service'; ( globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean } ).IS_REACT_ACT_ENVIRONMENT = true; jest.mock('../../../env', () => ({ recomaze_ai_personalization_env: {} })); jest.mock('../../../context/seo-badges.context', () => ({ useSeoBadges: () => ({ refresh: jest.fn() }), })); jest.mock('../../../service/seo/seo.service', () => ({ fetchSeoSettings: jest.fn(), fetchSeoFiles: jest.fn(), fetchSeoImportSources: jest.fn(), runSeoImportBatch: jest.fn(), switchSeoPlugin: jest.fn(), saveSeoRobots: jest.fn(), updateSeoSettings: jest.fn(), })); /** The settings before and after Rank Math's were brought over. */ const settings = (imported: boolean) => ({ titles: { knowledge_graph_type: 'organization', knowledge_graph_name: imported ? 'Recomaze Wear' : '', knowledge_graph_logo: '', og_default_image: '', social_facebook_url: '', social_twitter: '', social_profiles: '', }, general: { nofollow_external: imported, new_window_external: imported }, sitemap: { enabled: true }, modules: { redirects: { active: imported }, monitor_404: { active: imported }, image_seo: { active: false }, local_seo: { active: false }, }, llms: { description: '' }, post_types: {}, taxonomies: {}, }); const rankMath = { slug: 'rank-math', name: 'Rank Math SEO', active: true, available: true, counts: { settings: true, posts: 7, terms: 0, users: 0, redirects: 4, logs_404: 0, }, }; const flush = async (): Promise => { for (let i = 0; i < 5; i++) { // eslint-disable-next-line no-await-in-loop await act(async () => { await new Promise(resolve => setTimeout(resolve, 0)); }); } }; const click = async (label: string): Promise => { const button = Array.from(document.querySelectorAll('button')).find( item => item.textContent?.trim() === label ); if (!button) throw new Error(`No button "${label}"`); await act(async () => { button.click(); }); await flush(); }; describe('the setup wizard after an import', () => { let root: Root; let host: HTMLDivElement; beforeEach(() => { host = document.createElement('div'); document.body.appendChild(host); root = createRoot(host); (fetchSeoSettings as jest.Mock) .mockResolvedValueOnce(settings(false)) .mockResolvedValue(settings(true)); (fetchSeoFiles as jest.Mock).mockRejectedValue(new Error('not needed')); (fetchSeoImportSources as jest.Mock).mockResolvedValue({ sources: [rankMath], }); (runSeoImportBatch as jest.Mock).mockResolvedValue({ part: 'posts', offset: 0, next: 7, total: 7, done: true, }); (switchSeoPlugin as jest.Mock).mockResolvedValue({ deactivated: true, sources: [{ ...rankMath, active: false }], }); (updateSeoSettings as jest.Mock).mockImplementation(() => Promise.resolve(settings(true)) ); }); afterEach(() => { act(() => root.unmount()); host.remove(); jest.clearAllMocks(); }); it('shows and saves what the import brought, not what was there before', async () => { await act(async () => { root.render( ); }); await flush(); await click('Import & switch'); expect(switchSeoPlugin).toHaveBeenCalledWith('rank-math'); await click('Save and continue'); const name = Array.from(document.querySelectorAll('input')).find( input => input.value === 'Recomaze Wear' ); expect(name).toBeDefined(); await click('Save and continue'); expect(updateSeoSettings).toHaveBeenCalledWith( expect.objectContaining({ titles: expect.objectContaining({ knowledge_graph_name: 'Recomaze Wear', }), }) ); const saved = (updateSeoSettings as jest.Mock).mock.calls.map( ([patch]) => patch ); expect(saved.some(patch => patch.modules?.redirects === false)).toBe(false); }); it('says the plugin is off, counts in words, ticks the import and saves on Back', async () => { (runSeoImportBatch as jest.Mock).mockImplementation( (_slug: string, part: string) => Promise.resolve({ part, offset: 0, next: 0, total: { posts: 7, users: 1 }[part] ?? 0, done: true, }) ); await act(async () => { root.render( ); }); await flush(); expect(document.body.textContent).toContain( 'Rank Math SEO is active and prints the head' ); expect(document.body.textContent).not.toContain('changes nothing'); await click('Import & switch'); expect(document.body.textContent).toContain( 'Brought over: 7 pages with SEO fields, 1 author.' ); expect(document.body.textContent).toContain( 'Rank Math SEO is switched off now' ); expect(document.body.textContent).not.toContain( 'is active and prints the head' ); await click('Save and continue'); const first = document.querySelector( 'button[aria-label="Step 1: Bring your data over"]' ); expect(first?.textContent?.startsWith('1')).toBe(false); const before = (updateSeoSettings as jest.Mock).mock.calls.length; await click('Back'); expect((updateSeoSettings as jest.Mock).mock.calls.length).toBe(before + 1); }); });