Refine Media Manager Save Logic and UI
- Add failing global function verification test (GlobalFunctions.test.ts) and fix missing exports in global.ts.
- Refactor MediaManager.html UI:
- Implement
enderPlanHtml to standardize Plan (Details) and Execution views.
- Show 'Skipped' state for empty save phases.
- Visually decouple 'Sheet Update' from 'Reorder' phase.
- Separate 'Manual Link' operations into their own 'Linking' section in the plan view, distinct from Adoptions.
- Fix TypeErrors in
enderPlanHtml (undefined actions) and
enderMatch (missing DOM elements).
- Update MediaService.test.ts to match new filename constraints on reorder.
- Update mediaHandlers.test.ts to correctly spy on loose MediaService instances.
- Ensure all tests pass.
This commit is contained in:
81
src/test/GlobalFunctions.test.ts
Normal file
81
src/test/GlobalFunctions.test.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
describe('Global Function Exports', () => {
|
||||
const srcDir = path.resolve(__dirname, '../'); // Assumes src/test/GlobalFunctions.test.ts
|
||||
const globalFile = path.join(srcDir, 'global.ts');
|
||||
|
||||
// 1. Get all globally exported function names
|
||||
const getGlobalExports = (): Set<string> => {
|
||||
const content = fs.readFileSync(globalFile, 'utf-8');
|
||||
const regex = /;\(global as any\)\.(\w+)\s*=/g;
|
||||
const exports = new Set<string>();
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
exports.add(match[1]);
|
||||
}
|
||||
return exports;
|
||||
};
|
||||
|
||||
// 2. Find all google.script.run calls in HTML files
|
||||
const getFrontendCalls = (): Map<string, string> => {
|
||||
const calls = new Map<string, string>(); // functionName -> filename (for error msg)
|
||||
|
||||
const scanDir = (dir: string) => {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
scanDir(fullPath);
|
||||
} else if (file.endsWith('.html')) {
|
||||
const content = fs.readFileSync(fullPath, 'utf-8');
|
||||
// Matches:
|
||||
// google.script.run.myFunc()
|
||||
// google.script.run.withSuccessHandler(...).myFunc()
|
||||
// google.script.run.withFailureHandler(...).myFunc()
|
||||
// google.script.run.withSuccessHandler(...).withFailureHandler(...).myFunc()
|
||||
|
||||
// Regex strategy:
|
||||
// 1. Find "google.script.run"
|
||||
// 2. Consume optional handlers .with...(...)
|
||||
// 3. Capture the final function name .FunctionName(
|
||||
|
||||
const callRegex = /google\.script\.run(?:[\s\n]*\.(?:withSuccessHandler|withFailureHandler|withUserObject)\([^)]*\))*[\s\n]*\.(\w+)\s*\(/g;
|
||||
|
||||
let match;
|
||||
while ((match = callRegex.exec(content)) !== null) {
|
||||
const funcName = match[1];
|
||||
if (!['withSuccessHandler', 'withFailureHandler', 'withUserObject'].includes(funcName)) {
|
||||
calls.set(funcName, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scanDir(srcDir);
|
||||
return calls;
|
||||
};
|
||||
|
||||
test('All client-side google.script.run calls must be exported in global.ts', () => {
|
||||
const globalExports = getGlobalExports();
|
||||
const frontendCalls = getFrontendCalls();
|
||||
const missingQuery = [];
|
||||
|
||||
frontendCalls.forEach((filename, funcName) => {
|
||||
if (!globalExports.has(funcName)) {
|
||||
missingQuery.push(`${funcName} (called in ${filename})`);
|
||||
}
|
||||
});
|
||||
|
||||
if (missingQuery.length > 0) {
|
||||
throw new Error(
|
||||
`The following backend functions are called from the frontend but missing from src/global.ts:\n` +
|
||||
missingQuery.join('\n') +
|
||||
`\n\nPlease add them to src/global.ts like: ;(global as any).${missingQuery[0].split(' ')[0]} = ...`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user