PluginProbe
Extendify / 3.0.6
Extendify v3.0.6
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / Launch / api / __tests__ / addPageLinksToNav.test.js

addPageLinksToNav.test.js in Extendify 3.0.6, at src/Launch/api/__tests__/addPageLinksToNav.test.js

219 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { addPageLinksToNav } from '@launch/api/WPApi';
2
3 // Mock @wordpress/api-fetch to prevent actual API calls
4 let capturedNavContent = null;
5 jest.mock('@wordpress/api-fetch', () =>
6 jest.fn((options) => {
7 if (options.path?.includes('navigation')) {
8 capturedNavContent = options.data?.content || '';
9 }
10 return Promise.resolve({});
11 }),
12 );
13
14 // Helper to create a mock page object (created pages use originalSlug)
15 const createPage = (slug, id = Math.random(), isPlugin = false) => {
16 const data = {
17 id,
18 title: { rendered: slug.charAt(0).toUpperCase() + slug.slice(1) },
19 link: `https://example.com/${slug}`,
20 type: 'page',
21 };
22 return isPlugin ? { ...data, slug } : { ...data, originalSlug: slug };
23 };
24
25 // Helper to extract page labels from the navigation content
26 const extractLabels = (navContent) => {
27 const matches = navContent.matchAll(/"label":"([^"]+)"/g);
28 return [...matches].map((m) => m[1]);
29 };
30
31 // Helper to extract top-level labels (outside submenu blocks)
32 const extractTopLevelLabels = (navContent) => {
33 const withoutSubmenus = navContent.replace(
34 /<!-- wp:navigation-submenu[\s\S]*?<!-- \/wp:navigation-submenu -->/g,
35 '',
36 );
37 return extractLabels(withoutSubmenus);
38 };
39
40 // Helper to extract submenu labels
41 const extractSubmenuLabels = (navContent) => {
42 const submenuMatch = navContent.match(
43 /<!-- wp:navigation-submenu[\s\S]*?<!-- \/wp:navigation-submenu -->/,
44 );
45 return submenuMatch ? extractLabels(submenuMatch[0]) : [];
46 };
47
48 describe('addPageLinksToNav', () => {
49 beforeEach(() => {
50 capturedNavContent = null;
51 });
52
53 describe('sorting by navOrder', () => {
54 it('should sort pages by their navOrder', async () => {
55 const allPages = [
56 { slug: 'about' }, // navOrder: 19
57 { slug: 'services' }, // navOrder: 1
58 { slug: 'contact' }, // navOrder: 5
59 { slug: 'pricing' }, // navOrder: 4
60 ];
61 const createdPages = allPages.map((p) => createPage(p.slug));
62
63 await addPageLinksToNav('nav-1', allPages, createdPages);
64
65 const labels = extractLabels(capturedNavContent);
66 // Sorted by navOrder: Services (1), Pricing (4), About (19)
67 expect(labels).toEqual(['Services', 'Pricing', 'About', 'Contact']);
68 });
69
70 it('should resolve plugin page slugs via aliases (e.g., shop -> products)', async () => {
71 const allPages = [{ slug: 'services' }, { slug: 'about' }];
72 const createdPages = [createPage('services'), createPage('about')];
73 const pluginPages = [createPage('shop', null, true)]; // shop is alias for products (navOrder: 2)
74
75 await addPageLinksToNav('nav-1', allPages, createdPages, pluginPages);
76
77 const labels = extractLabels(capturedNavContent);
78 // Services (1), Shop/Products (2), About (19)
79 expect(labels).toEqual(['Services', 'Shop', 'About']);
80 });
81
82 it('should place unknown slugs after sorted pages but before contact', async () => {
83 const allPages = [{ slug: 'services' }, { slug: 'contact' }];
84 const createdPages = [createPage('services'), createPage('contact')];
85 const pluginPages = [
86 createPage('unknown-a', null, true),
87 createPage('unknown-b', null, true),
88 ];
89
90 await addPageLinksToNav('nav-1', allPages, createdPages, pluginPages);
91
92 const labels = extractLabels(capturedNavContent);
93 // Services (1), then unknowns (maxOrder), then Contact (always last)
94 expect(labels).toEqual(['Services', 'Unknown-a', 'Unknown-b', 'Contact']);
95 });
96 });
97
98 describe('contact page positioning', () => {
99 it('should place contact at end when 6 or fewer pages (no submenu)', async () => {
100 const allPages = [
101 { slug: 'services' }, // 1
102 { slug: 'products' }, // 2
103 { slug: 'book' }, // 3
104 { slug: 'pricing' }, // 4
105 { slug: 'about' }, // 19
106 { slug: 'contact' }, // moved to last
107 ];
108 const createdPages = allPages.map((p) => createPage(p.slug));
109
110 await addPageLinksToNav('nav-1', allPages, createdPages);
111
112 const labels = extractLabels(capturedNavContent);
113
114 // All 6 should be top-level with contact last
115 expect(labels).toEqual([
116 'Services',
117 'Products',
118 'Book',
119 'Pricing',
120 'About',
121 'Contact',
122 ]);
123 expect(labels[5]).toBe('Contact');
124
125 // No submenu when exactly 6 pages
126 expect(capturedNavContent).not.toContain('wp:navigation-submenu');
127 });
128
129 it('should place contact at index 4 with remaining pages in submenu when 7+ pages', async () => {
130 const allPages = [
131 { slug: 'services' }, // 1
132 { slug: 'products' }, // 2
133 { slug: 'book' }, // 3
134 { slug: 'pricing' }, // 4
135 { slug: 'blog' }, // 18
136 { slug: 'about' }, // 19
137 { slug: 'contact' }, // moved to 5th (index 4)
138 ];
139 const createdPages = allPages.map((p) => createPage(p.slug));
140
141 await addPageLinksToNav('nav-1', allPages, createdPages);
142
143 const topLevelLabels = extractTopLevelLabels(capturedNavContent);
144 const submenuLabels = extractSubmenuLabels(capturedNavContent);
145
146 // Top 5: Services, Products, Book, Pricing, Contact (promoted to last top-level)
147 expect(topLevelLabels).toHaveLength(5);
148 expect(topLevelLabels).toEqual([
149 'Services',
150 'Products',
151 'Book',
152 'Pricing',
153 'Contact',
154 ]);
155 expect(topLevelLabels[4]).toBe('Contact');
156
157 // Submenu should contain remaining pages (Blog, About) plus "More" label
158 expect(capturedNavContent).toContain('wp:navigation-submenu');
159 expect(submenuLabels).toContain('More');
160 expect(submenuLabels).toContain('Blog');
161 expect(submenuLabels).toContain('About');
162
163 // Contact should NOT be in submenu
164 expect(submenuLabels).not.toContain('Contact');
165 });
166
167 it('should handle case when no contact page exists', async () => {
168 const allPages = [
169 { slug: 'services' },
170 { slug: 'products' },
171 { slug: 'about' },
172 ];
173 const createdPages = allPages.map((p) => createPage(p.slug));
174
175 await addPageLinksToNav('nav-1', allPages, createdPages);
176
177 const labels = extractLabels(capturedNavContent);
178 expect(labels).toEqual(['Services', 'Products', 'About']);
179 expect(labels).not.toContain('Contact');
180 });
181
182 it('should recognize contact page by alias (contact-form)', async () => {
183 const allPages = [{ slug: 'services' }, { slug: 'about' }];
184 const createdPages = [createPage('services'), createPage('about')];
185 const pluginPages = [createPage('contact-form', null, true)]; // alias for contact
186
187 await addPageLinksToNav('nav-1', allPages, createdPages, pluginPages);
188
189 const labels = extractLabels(capturedNavContent);
190 // Contact-form should be treated as contact and placed last
191 expect(labels).toEqual(['Services', 'About', 'Contact-form']);
192 expect(labels[labels.length - 1]).toBe('Contact-form');
193 });
194 });
195
196 describe('edge cases', () => {
197 it('should handle empty pages array', async () => {
198 await addPageLinksToNav('nav-1', [], []);
199
200 expect(capturedNavContent).toBe('');
201 });
202
203 it('should exclude home page from navigation', async () => {
204 const allPages = [
205 { slug: 'home' },
206 { slug: 'services' },
207 { slug: 'contact' },
208 ];
209 const createdPages = allPages.map((p) => createPage(p.slug));
210
211 await addPageLinksToNav('nav-1', allPages, createdPages);
212
213 const labels = extractLabels(capturedNavContent);
214 expect(labels).not.toContain('Home');
215 expect(labels).toEqual(['Services', 'Contact']);
216 });
217 });
218 });
219