17c98c357SEvan Bacon/* eslint-env jest */
27c98c357SEvan Baconimport execa from 'execa';
37c98c357SEvan Baconimport fs from 'fs-extra';
47c98c357SEvan Baconimport klawSync from 'klaw-sync';
57c98c357SEvan Baconimport path from 'path';
67c98c357SEvan Bacon
77c98c357SEvan Baconimport { runExportSideEffects } from './export-side-effects';
87c98c357SEvan Baconimport { bin, getPageHtml, getRouterE2ERoot } from '../utils';
97c98c357SEvan Bacon
107c98c357SEvan BaconrunExportSideEffects();
117c98c357SEvan Bacon
127c98c357SEvan Bacondescribe('static-rendering with a custom base path', () => {
137c98c357SEvan Bacon  const projectRoot = getRouterE2ERoot();
147c98c357SEvan Bacon  const outputName = 'dist-static-rendering-asset-prefix';
157c98c357SEvan Bacon  const outputDir = path.join(projectRoot, outputName);
167c98c357SEvan Bacon
177c98c357SEvan Bacon  beforeAll(
187c98c357SEvan Bacon    async () => {
197c98c357SEvan Bacon      const basePath = '/one/two';
207c98c357SEvan Bacon      process.env.EXPO_E2E_BASE_PATH = basePath;
217c98c357SEvan Bacon      await execa('node', [bin, 'export', '-p', 'web', '--clear', '--output-dir', outputName], {
227c98c357SEvan Bacon        cwd: projectRoot,
237c98c357SEvan Bacon        env: {
247c98c357SEvan Bacon          NODE_ENV: 'production',
257c98c357SEvan Bacon          EXPO_E2E_BASE_PATH: basePath,
26*46f023faSEvan Bacon          EXPO_USE_STATIC: 'static',
277c98c357SEvan Bacon          E2E_ROUTER_SRC: 'static-rendering',
287c98c357SEvan Bacon          E2E_ROUTER_ASYNC: 'development',
297c98c357SEvan Bacon        },
307c98c357SEvan Bacon      });
317c98c357SEvan Bacon    },
327c98c357SEvan Bacon    // Could take 45s depending on how fast the bundler resolves
337c98c357SEvan Bacon    560 * 1000
347c98c357SEvan Bacon  );
357c98c357SEvan Bacon
367c98c357SEvan Bacon  it(
377c98c357SEvan Bacon    'has expected files',
387c98c357SEvan Bacon    async () => {
397c98c357SEvan Bacon      // List output files with sizes for snapshotting.
407c98c357SEvan Bacon      // This is to make sure that any changes to the output are intentional.
417c98c357SEvan Bacon      // Posix path formatting is used to make paths the same across OSes.
427c98c357SEvan Bacon      const files = klawSync(outputDir)
437c98c357SEvan Bacon        .map((entry) => {
447c98c357SEvan Bacon          if (entry.path.includes('node_modules') || !entry.stats.isFile()) {
457c98c357SEvan Bacon            return null;
467c98c357SEvan Bacon          }
477c98c357SEvan Bacon          return path.posix.relative(outputDir, entry.path);
487c98c357SEvan Bacon        })
497c98c357SEvan Bacon        .filter(Boolean);
507c98c357SEvan Bacon
517c98c357SEvan Bacon      expect(
527c98c357SEvan Bacon        files.find((file) => file?.match(/\_expo\/static\/js\/web\/index-.*\.js/))
537c98c357SEvan Bacon      ).toBeDefined();
547c98c357SEvan Bacon
557c98c357SEvan Bacon      // The wrapper should not be included as a route.
567c98c357SEvan Bacon      expect(files).not.toContain('+html.html');
577c98c357SEvan Bacon      expect(files).not.toContain('_layout.html');
587c98c357SEvan Bacon
597c98c357SEvan Bacon      // Injected by framework
607c98c357SEvan Bacon      expect(files).toContain('_sitemap.html');
617c98c357SEvan Bacon      expect(files).toContain('[...404].html');
627c98c357SEvan Bacon
637c98c357SEvan Bacon      // Normal routes
647c98c357SEvan Bacon      expect(files).toContain('about.html');
657c98c357SEvan Bacon      expect(files).toContain('index.html');
667c98c357SEvan Bacon      expect(files).toContain('styled.html');
677c98c357SEvan Bacon      expect(files).toContain('links.html');
687c98c357SEvan Bacon
697c98c357SEvan Bacon      // generateStaticParams values
707c98c357SEvan Bacon      expect(files).toContain('[post].html');
717c98c357SEvan Bacon      expect(files).toContain('welcome-to-the-universe.html');
727c98c357SEvan Bacon      expect(files).toContain('other.html');
737c98c357SEvan Bacon    },
747c98c357SEvan Bacon    5 * 1000
757c98c357SEvan Bacon  );
767c98c357SEvan Bacon
777c98c357SEvan Bacon  it(
787c98c357SEvan Bacon    'writes assets with prefix',
797c98c357SEvan Bacon    async () => {
807c98c357SEvan Bacon      const indexHtml = await getPageHtml(outputDir, 'index.html');
817c98c357SEvan Bacon
827c98c357SEvan Bacon      const jsFiles = indexHtml.querySelectorAll('script').map((script) => script.attributes.src);
837c98c357SEvan Bacon      expect(jsFiles).toEqual([
847c98c357SEvan Bacon        expect.stringMatching(/\/one\/two\/_expo\/static\/js\/web\/index-.*\.js/),
857c98c357SEvan Bacon      ]);
867c98c357SEvan Bacon
877c98c357SEvan Bacon      const links = indexHtml.querySelectorAll('html > head > link').filter((link) => {
887c98c357SEvan Bacon        // Fonts are tested elsewhere
897c98c357SEvan Bacon        return link.attributes.as !== 'font';
907c98c357SEvan Bacon      });
917c98c357SEvan Bacon
927c98c357SEvan Bacon      const cssFiles = links.map((link) => link.attributes.href);
937c98c357SEvan Bacon
947c98c357SEvan Bacon      cssFiles.forEach((src) => {
957c98c357SEvan Bacon        // Linked to the expected static location
967c98c357SEvan Bacon        expect(src).toMatch(/^\/one\/two\/_expo\/static\/css\/.*\.css$/);
977c98c357SEvan Bacon      });
987c98c357SEvan Bacon
997c98c357SEvan Bacon      const fontLinks = indexHtml.querySelectorAll('html > head > link[as="font"]');
1007c98c357SEvan Bacon
1017c98c357SEvan Bacon      const fontFiles = fontLinks.map((link) => link.attributes.href);
1027c98c357SEvan Bacon
1037c98c357SEvan Bacon      fontFiles.forEach((src) => {
1047c98c357SEvan Bacon        // Linked to the expected static location
1057c98c357SEvan Bacon        expect(src).toMatch(/^\/one\/two\/assets\/.*\.ttf/);
1067c98c357SEvan Bacon      });
1077c98c357SEvan Bacon
1087c98c357SEvan Bacon      for (const file of [...cssFiles, ...jsFiles]) {
1097c98c357SEvan Bacon        expect(
1107c98c357SEvan Bacon          fs.existsSync(
1117c98c357SEvan Bacon            path.join(
1127c98c357SEvan Bacon              outputDir,
1137c98c357SEvan Bacon              // If we strip the asset prefix, the file should exist.
1147c98c357SEvan Bacon              file.replace(/^\/one\/two/, '')
1157c98c357SEvan Bacon            )
1167c98c357SEvan Bacon          )
1177c98c357SEvan Bacon        ).toBe(true);
1187c98c357SEvan Bacon      }
1197c98c357SEvan Bacon    },
1207c98c357SEvan Bacon    5 * 1000
1217c98c357SEvan Bacon  );
1227c98c357SEvan Bacon
1237c98c357SEvan Bacon  it(
1247c98c357SEvan Bacon    'supports usePathname in +html files',
1257c98c357SEvan Bacon    async () => {
1267c98c357SEvan Bacon      const page = await fs.readFile(path.join(outputDir, 'index.html'), 'utf8');
1277c98c357SEvan Bacon
1287c98c357SEvan Bacon      expect(page).toContain('<meta name="custom-value" content="value"/>');
1297c98c357SEvan Bacon
1307c98c357SEvan Bacon      // Root element
1317c98c357SEvan Bacon      expect(page).toContain('<div id="root">');
1327c98c357SEvan Bacon
1337c98c357SEvan Bacon      expect(
1347c98c357SEvan Bacon        (await getPageHtml(outputDir, 'about.html')).querySelector(
1357c98c357SEvan Bacon          'html > head > meta[name="expo-e2e-pathname"]'
1367c98c357SEvan Bacon        )?.attributes.content
1377c98c357SEvan Bacon      ).toBe('/about');
1387c98c357SEvan Bacon
1397c98c357SEvan Bacon      expect(
1407c98c357SEvan Bacon        (await getPageHtml(outputDir, 'index.html')).querySelector(
1417c98c357SEvan Bacon          'html > head > meta[name="expo-e2e-pathname"]'
1427c98c357SEvan Bacon        )?.attributes.content
1437c98c357SEvan Bacon      ).toBe('/');
1447c98c357SEvan Bacon
1457c98c357SEvan Bacon      expect(
1467c98c357SEvan Bacon        (await getPageHtml(outputDir, 'welcome-to-the-universe.html')).querySelector(
1477c98c357SEvan Bacon          'html > head > meta[name="expo-e2e-pathname"]'
1487c98c357SEvan Bacon        )?.attributes.content
1497c98c357SEvan Bacon      ).toBe('/welcome-to-the-universe');
1507c98c357SEvan Bacon    },
1517c98c357SEvan Bacon    5 * 1000
1527c98c357SEvan Bacon  );
1537c98c357SEvan Bacon
1547c98c357SEvan Bacon  it(
1557c98c357SEvan Bacon    'supports basePath in Links',
1567c98c357SEvan Bacon    async () => {
1577c98c357SEvan Bacon      expect(
1587c98c357SEvan Bacon        (await getPageHtml(outputDir, 'links.html')).querySelector(
1597c98c357SEvan Bacon          'body > #root a[data-testid="links-one"]'
1607c98c357SEvan Bacon        )?.attributes.href
1617c98c357SEvan Bacon      ).toBe('/one/two/about');
1627c98c357SEvan Bacon    },
1637c98c357SEvan Bacon    5 * 1000
1647c98c357SEvan Bacon  );
1657c98c357SEvan Bacon});
166