Skip to content

Commit 49c2a7b

Browse files
hiteshkrmsftCopilot
andcommitted
Track Playwright inspection scripts; only ignore auth-state.json
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent cb8aaf7 commit 49c2a7b

3 files changed

Lines changed: 215 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,5 +103,4 @@ build/
103103

104104
# Firebase sensitive config (if you have API keys)
105105
# www/firebaseConfig.js
106-
scripts/
107-
auth-state.json
106+
scripts/auth-state.json

scripts/inspect-app.js

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/**
2+
* Headless inspector. Loads the deployed app using the saved auth state,
3+
* captures the BUILD_ID banner from the console, then runs window.debugStock
4+
* for one or more item names and prints what calculateStock() returns plus
5+
* the running tail of the timeline.
6+
*
7+
* Run with:
8+
* node scripts/inspect-app.js # defaults to Gehu
9+
* node scripts/inspect-app.js Gehu "Gehu dagi"
10+
* APP_URL=https://staging.example node scripts/inspect-app.js
11+
*/
12+
const { chromium } = require('playwright');
13+
const path = require('path');
14+
const fs = require('fs');
15+
16+
const APP_URL = process.env.APP_URL || 'https://aadhat-management.web.app';
17+
const STATE_PATH = path.resolve(__dirname, 'auth-state.json');
18+
const ITEMS = process.argv.slice(2).filter(Boolean);
19+
if (!ITEMS.length) ITEMS.push('Gehu');
20+
21+
function colorize(s) {
22+
return s
23+
.replace(/\u001b\[[0-9;]*m/g, '')
24+
.replace(/%c/g, '')
25+
.trim();
26+
}
27+
28+
async function main() {
29+
if (!fs.existsSync(STATE_PATH)) {
30+
console.error(`[inspect] no auth state at ${STATE_PATH}.`);
31+
console.error('[inspect] run: node scripts/save-login.js first.');
32+
process.exit(2);
33+
}
34+
35+
const browser = await chromium.launch({ headless: true });
36+
const ctx = await browser.newContext({ storageState: STATE_PATH });
37+
const page = await ctx.newPage();
38+
39+
const pageErrors = [];
40+
page.on('pageerror', (err) => pageErrors.push(String(err)));
41+
42+
let buildIdSeen = null;
43+
const earlyLogs = [];
44+
page.on('console', (msg) => {
45+
const text = colorize(msg.text());
46+
if (earlyLogs.length < 80) earlyLogs.push(`[${msg.type()}] ${text}`);
47+
const m = text.match(/\[Aadhat\]\s*BUILD\s+([\w.-]+)/i);
48+
if (m && !buildIdSeen) buildIdSeen = m[1];
49+
});
50+
51+
console.log(`[inspect] navigating to ${APP_URL}`);
52+
await page.goto(APP_URL, { waitUntil: 'domcontentloaded' });
53+
54+
// Wait a bit for the SPA + Firebase data load.
55+
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
56+
await page.waitForTimeout(3000);
57+
58+
// Try to read BUILD from the global if console capture missed it.
59+
const buildFromWindow = await page.evaluate(() => window.__AADHAT_BUILD__ || null);
60+
61+
console.log('');
62+
console.log('=== Build info ===');
63+
console.log('BUILD from console log:', buildIdSeen || '(not seen)');
64+
console.log('BUILD from window.__AADHAT_BUILD__:', buildFromWindow || '(undefined)');
65+
if (!buildIdSeen && !buildFromWindow) {
66+
console.log('!!! No BUILD id — browser is loading STALE main.js (cached SW).');
67+
}
68+
69+
if (pageErrors.length) {
70+
console.log('');
71+
console.log('=== Page errors ===');
72+
pageErrors.forEach((e) => console.log(' ', e));
73+
}
74+
75+
// Wait until calculateStock / debugStock are available.
76+
const ready = await page.waitForFunction(
77+
() => typeof window.calculateStock === 'function' || typeof window.debugStock === 'function',
78+
{ timeout: 20000 }
79+
).then(() => true).catch(() => false);
80+
81+
if (!ready) {
82+
console.log('');
83+
console.log('!!! window.calculateStock / debugStock never became available.');
84+
console.log('=== First ~80 console messages ===');
85+
earlyLogs.forEach((l) => console.log(' ', l));
86+
await browser.close();
87+
process.exit(3);
88+
}
89+
90+
for (const name of ITEMS) {
91+
console.log('');
92+
console.log('================================================');
93+
console.log(`Inspecting item: "${name}"`);
94+
console.log('================================================');
95+
96+
const summary = await page.evaluate(async (itemName) => {
97+
const out = { name: itemName, calculated: null, matchedKey: null, errors: [] };
98+
try {
99+
if (typeof window.calculateStock === 'function') {
100+
const all = await window.calculateStock();
101+
// calculateStock returns an array of { itemId, itemName, quantity, rate, ... }
102+
if (Array.isArray(all)) {
103+
out.calculated = all.filter((row) =>
104+
(row.itemName || '').toLowerCase().includes(itemName.toLowerCase())
105+
);
106+
} else if (all && typeof all === 'object') {
107+
// Some implementations return a map keyed by item id/name.
108+
out.calculated = Object.entries(all)
109+
.filter(([k, v]) => (k + ' ' + (v?.itemName || '')).toLowerCase().includes(itemName.toLowerCase()))
110+
.map(([k, v]) => ({ key: k, ...v }));
111+
} else {
112+
out.calculated = all;
113+
}
114+
}
115+
} catch (e) {
116+
out.errors.push(`calculateStock: ${String(e)}`);
117+
}
118+
try {
119+
const items = (window.AppState?.items || []).filter((i) =>
120+
(i.name || '').toLowerCase().includes(itemName.toLowerCase())
121+
);
122+
out.matchedCatalogueItems = items.map((i) => ({
123+
id: i.id,
124+
name: i.name,
125+
unit: i.unit,
126+
}));
127+
} catch (e) {
128+
out.errors.push(`AppState read: ${String(e)}`);
129+
}
130+
return out;
131+
}, name);
132+
133+
console.log('calculateStock():', JSON.stringify(summary.calculated, null, 2));
134+
console.log('Catalogue rows matching name:', JSON.stringify(summary.matchedCatalogueItems, null, 2));
135+
if (summary.errors.length) console.log('Errors:', summary.errors);
136+
137+
// Capture debugStock console output by hooking before invoking.
138+
const debugLogs = [];
139+
const onMsg = (msg) => debugLogs.push(`[${msg.type()}] ${colorize(msg.text())}`);
140+
page.on('console', onMsg);
141+
142+
const ranDebug = await page.evaluate((itemName) => {
143+
if (typeof window.debugStock === 'function') {
144+
window.debugStock(itemName);
145+
return true;
146+
}
147+
return false;
148+
}, name);
149+
150+
await page.waitForTimeout(1500);
151+
page.off('console', onMsg);
152+
153+
if (!ranDebug) {
154+
console.log('(no window.debugStock available)');
155+
} else {
156+
console.log('--- debugStock output (last 60 lines) ---');
157+
debugLogs.slice(-60).forEach((l) => console.log(' ', l));
158+
}
159+
}
160+
161+
await browser.close();
162+
console.log('');
163+
console.log('[inspect] done.');
164+
}
165+
166+
main().catch((err) => {
167+
console.error('[inspect] failed:', err);
168+
process.exit(1);
169+
});

scripts/save-login.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* One-time login helper. Opens a real Chromium window, you log in by hand,
3+
* press Enter in the terminal, and your auth state is saved to
4+
* scripts/auth-state.json so future inspect-app.js runs are headless.
5+
*
6+
* Run with:
7+
* node scripts/save-login.js
8+
*/
9+
const { chromium } = require('playwright');
10+
const path = require('path');
11+
const readline = require('readline');
12+
13+
const APP_URL = process.env.APP_URL || 'https://aadhat-management.web.app';
14+
const STATE_PATH = path.resolve(__dirname, 'auth-state.json');
15+
16+
async function main() {
17+
const browser = await chromium.launch({ headless: false });
18+
const ctx = await browser.newContext();
19+
const page = await ctx.newPage();
20+
21+
console.log(`[save-login] opening ${APP_URL}`);
22+
await page.goto(APP_URL, { waitUntil: 'domcontentloaded' });
23+
24+
console.log('[save-login] ▶ Log in in the opened window.');
25+
console.log('[save-login] ▶ When you are fully signed in and the dashboard is visible,');
26+
console.log('[save-login] ▶ come back here and press Enter to save the session.');
27+
28+
await new Promise((resolve) => {
29+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
30+
rl.question('Press Enter when logged in… ', () => {
31+
rl.close();
32+
resolve();
33+
});
34+
});
35+
36+
await ctx.storageState({ path: STATE_PATH });
37+
console.log(`[save-login] saved auth state to ${STATE_PATH}`);
38+
39+
await browser.close();
40+
}
41+
42+
main().catch((err) => {
43+
console.error('[save-login] failed:', err);
44+
process.exit(1);
45+
});

0 commit comments

Comments
 (0)