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