# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: safe_bottom.spec.js >> Safe-bottom — link tag >> installation HTML loads safe-bottom.css
- Location: tests/safe_bottom.spec.js:70:5

# Error details

```
Error: expect(received).toBe(expected) // Object.is equality

Expected: 200
Received: 404
```

# Test source

```ts
  1   | // safe_bottom.spec.js — per-PWA bottom-safe-area tests
  2   | //
  3   | // For every PWA that gates on Hub session, verify:
  4   | //   1. The page returns 200
  5   | //   2. The /shared/safe-bottom.css link tag is present in the HTML
  6   | //   3. Computed padding-bottom on the main scrolling container is ≥ 100px
  7   | //      (the value our shared CSS guarantees, plus iOS safe area)
  8   | //
  9   | // This catches future regressions where a PWA forgets to load the shared CSS
  10  | // or accidentally overrides padding-bottom too tightly.
  11  | const { test, expect, devices, request } = require('@playwright/test');
  12  | 
  13  | const BASE = 'https://srv1111289.hstgr.cloud';
  14  | 
  15  | // (URL, expected scrolling-container selector — first that exists on the page is checked)
  16  | const PWAS = [
  17  |   { name: 'expense',           url: '/expense/',                selectors: ['.body', '.scr', '.screen'] },
  18  |   { name: 'recce',             url: '/recce/',                  selectors: ['.body', '.scr'] },
  19  |   { name: 'installation',      url: '/installation/current/',   selectors: ['.body', '.scr'] },
  20  |   { name: 'finance',           url: '/finance/',                selectors: ['.body', '.scr'] },
  21  |   { name: 'finance-upi',       url: '/finance/upi/',            selectors: ['.body', '.scr'] },
  22  |   { name: 'finance-credit-card', url: '/finance/credit-card/',  selectors: ['.body', '.scr'] },
  23  |   { name: 'finance-custodian', url: '/finance/custodian/',      selectors: ['.body', '.scr'] },
  24  |   { name: 'finance-vendors',   url: '/finance/vendors/',        selectors: ['.body', '.scr'] },
  25  |   { name: 'sales',             url: '/sales/',                  selectors: ['.body', '.screen'] },
  26  |   { name: 'sales-customers',   url: '/sales/customers/',        selectors: ['.body', '.scr'] },
  27  |   { name: 'stores',            url: '/stores/',                 selectors: ['.body', '.scr'] },
  28  |   { name: 'hr',                url: '/hr/',                     selectors: ['.body', '.scr'] },
  29  |   { name: 'vehicle',           url: '/vehicle/',                selectors: ['.body', '.scr'] },
  30  |   { name: 'admin',             url: '/admin/',                  selectors: ['.body', '.scr'] },
  31  |   { name: 'activity',          url: '/activity/',               selectors: ['.body', '.scr'] },
  32  |   { name: 'production',        url: '/production/',             selectors: ['.body', '.scr'] },
  33  |   { name: 'tour-planner',      url: '/tour-planner/',           selectors: ['.body', '.scr'] },
  34  |   { name: 'tour-pg',           url: '/tour-pg/',                selectors: ['.scr', '.body'] },
  35  |   { name: 'hub',               url: '/hub/',                    selectors: ['.body', '.scr', '.screen'] },
  36  | ];
  37  | 
  38  | test.use({ ...devices['Pixel 6'] });
  39  | 
  40  | function injectSession(page, id = 'harish', name = 'Harish Kumar Lal') {
  41  |   return page.addInitScript(({ id, name }) => {
  42  |     localStorage.setItem('lm360-session', JSON.stringify({
  43  |       empId: id, name, role: 'admin', loginAt: new Date().toISOString()
  44  |     }));
  45  |   }, { id, name });
  46  | }
  47  | 
  48  | // ──────────────────────────────────────────────────────────────
  49  | // SHARED CSS PRESENT
  50  | // ──────────────────────────────────────────────────────────────
  51  | test.describe('Safe-bottom — shared CSS file', () => {
  52  |   test('/shared/safe-bottom.css returns 200 and contains :root rule', async () => {
  53  |     const ctx = await request.newContext();
  54  |     const r = await ctx.get(`${BASE}/shared/safe-bottom.css`);
  55  |     expect(r.status()).toBe(200);
  56  |     const css = await r.text();
  57  |     expect(css).toContain('--safe-bottom-min');
  58  |     expect(css).toContain('100px');
  59  |     expect(css).toContain('env(safe-area-inset-bottom');
  60  |     expect(css).toContain('!important');
  61  |     await ctx.dispose();
  62  |   });
  63  | });
  64  | 
  65  | // ──────────────────────────────────────────────────────────────
  66  | // PER-PWA: LINK TAG PRESENT
  67  | // ──────────────────────────────────────────────────────────────
  68  | test.describe('Safe-bottom — link tag', () => {
  69  |   for (const pwa of PWAS) {
  70  |     test(`${pwa.name} HTML loads safe-bottom.css`, async () => {
  71  |       const ctx = await request.newContext();
  72  |       const r = await ctx.get(BASE + pwa.url);
> 73  |       expect(r.status()).toBe(200);
      |                          ^ Error: expect(received).toBe(expected) // Object.is equality
  74  |       const html = await r.text();
  75  |       expect(html).toContain('/shared/safe-bottom.css');
  76  |       await ctx.dispose();
  77  |     });
  78  |   }
  79  | });
  80  | 
  81  | // ──────────────────────────────────────────────────────────────
  82  | // PER-PWA: COMPUTED PADDING-BOTTOM IS ENFORCED
  83  | // ──────────────────────────────────────────────────────────────
  84  | test.describe('Safe-bottom — computed padding-bottom ≥ 100px', () => {
  85  |   for (const pwa of PWAS) {
  86  |     test(`${pwa.name} main container has ≥100px bottom padding`, async ({ page }) => {
  87  |       await injectSession(page);
  88  |       await page.goto(BASE + pwa.url, { waitUntil: 'domcontentloaded' });
  89  |       await page.waitForTimeout(500);
  90  | 
  91  |       // Find the first selector that has an actual element rendered + sized
  92  |       const result = await page.evaluate((selectors) => {
  93  |         for (const sel of selectors) {
  94  |           const el = document.querySelector(sel);
  95  |           if (!el) continue;
  96  |           const cs = getComputedStyle(el);
  97  |           const pad = parseFloat(cs.paddingBottom);
  98  |           if (isNaN(pad)) continue;
  99  |           return { selector: sel, padding: pad };
  100 |         }
  101 |         return { selector: null, padding: -1 };
  102 |       }, pwa.selectors);
  103 | 
  104 |       if (result.selector === null) {
  105 |         // PWA might use a different container (e.g. only login screen visible).
  106 |         // Fail soft — the link-tag test already proves the CSS is loaded.
  107 |         test.skip(true, `No matching container for ${pwa.name} (probably login gate)`);
  108 |         return;
  109 |       }
  110 | 
  111 |       expect(result.padding, `${pwa.name} (${result.selector}) padding-bottom = ${result.padding}px`).toBeGreaterThanOrEqual(100);
  112 |     });
  113 |   }
  114 | });
  115 | 
```