Add multi-account OAuth, Obsidian integration, product assets, and test tooling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
208
product/site/smoke-test.cjs
Normal file
208
product/site/smoke-test.cjs
Normal file
@@ -0,0 +1,208 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const vm = require("vm");
|
||||
|
||||
const scriptPath = path.join(__dirname, "script.js");
|
||||
const scriptSource = fs.readFileSync(scriptPath, "utf8");
|
||||
|
||||
function createClassList() {
|
||||
const values = new Set();
|
||||
return {
|
||||
toggle(name, enabled) {
|
||||
if (enabled) values.add(name);
|
||||
else values.delete(name);
|
||||
},
|
||||
has(name) {
|
||||
return values.has(name);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createForm(valid, entries) {
|
||||
const listeners = {};
|
||||
let resetCount = 0;
|
||||
return {
|
||||
__entries: entries,
|
||||
reportValidity() {
|
||||
return valid;
|
||||
},
|
||||
reset() {
|
||||
resetCount += 1;
|
||||
},
|
||||
get resetCount() {
|
||||
return resetCount;
|
||||
},
|
||||
addEventListener(type, handler) {
|
||||
listeners[type] = handler;
|
||||
},
|
||||
dispatch(type, event) {
|
||||
if (!listeners[type]) throw new Error(`missing listener: ${type}`);
|
||||
return listeners[type](event);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createButton() {
|
||||
const listeners = {};
|
||||
return {
|
||||
addEventListener(type, handler) {
|
||||
listeners[type] = handler;
|
||||
},
|
||||
dispatch(type, event) {
|
||||
if (!listeners[type]) throw new Error(`missing listener: ${type}`);
|
||||
return listeners[type](event);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createEnv({ valid = true, fetchOk = true, clipboardFails = false } = {}) {
|
||||
const timers = [];
|
||||
const output = { textContent: "", classList: createClassList() };
|
||||
const heroAnimation = {
|
||||
src: "./squaremcp_launch.gif",
|
||||
dataset: {
|
||||
posterSrc: "./squaremcp_launch_poster.png",
|
||||
playMs: "9600",
|
||||
},
|
||||
};
|
||||
const entries = [
|
||||
["name", "Casey"],
|
||||
["email", "casey@example.com"],
|
||||
["company", "SquareMCP Labs"],
|
||||
["role", "Founder"],
|
||||
["use_case", "Internal support copilot"],
|
||||
["timeline", "Within 2 weeks"],
|
||||
["systems", "Postgres, internal REST APIs"],
|
||||
["requirements", "Audit logs and SSO"],
|
||||
];
|
||||
const form = createForm(valid, entries);
|
||||
const button = createButton();
|
||||
const fetchCalls = [];
|
||||
let clipboardText = null;
|
||||
|
||||
const context = {
|
||||
console,
|
||||
window: {
|
||||
setTimeout(fn, ms) {
|
||||
timers.push({ fn, ms });
|
||||
return timers.length;
|
||||
},
|
||||
},
|
||||
document: {
|
||||
getElementById(id) {
|
||||
if (id === "pilotIntakeForm") return form;
|
||||
if (id === "pilotOutput") return output;
|
||||
if (id === "copyRequestButton") return button;
|
||||
if (id === "heroAnimation") return heroAnimation;
|
||||
return null;
|
||||
},
|
||||
},
|
||||
navigator: {
|
||||
clipboard: {
|
||||
async writeText(value) {
|
||||
if (clipboardFails) throw new Error("clipboard denied");
|
||||
clipboardText = value;
|
||||
},
|
||||
},
|
||||
},
|
||||
fetch: async (url, options) => {
|
||||
fetchCalls.push({ url, options });
|
||||
if (!fetchOk) {
|
||||
return {
|
||||
ok: false,
|
||||
status: 500,
|
||||
async json() {
|
||||
return { error: "submit failed" };
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
status: 201,
|
||||
async json() {
|
||||
return { ok: true, request_id: "req-123" };
|
||||
},
|
||||
};
|
||||
},
|
||||
FormData: class MockFormData {
|
||||
constructor(target) {
|
||||
this.target = target;
|
||||
}
|
||||
entries() {
|
||||
return this.target.__entries[Symbol.iterator]();
|
||||
}
|
||||
[Symbol.iterator]() {
|
||||
return this.entries();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
vm.createContext(context);
|
||||
vm.runInContext(scriptSource, context, { filename: "script.js" });
|
||||
|
||||
return {
|
||||
form,
|
||||
button,
|
||||
output,
|
||||
heroAnimation,
|
||||
timers,
|
||||
fetchCalls,
|
||||
getClipboardText: () => clipboardText,
|
||||
};
|
||||
}
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) throw new Error(message);
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const validEnv = createEnv();
|
||||
assert(validEnv.timers.length === 1, "hero animation timer missing");
|
||||
assert(validEnv.timers[0].ms === 9600, "hero animation timer mismatch");
|
||||
validEnv.timers[0].fn();
|
||||
assert(validEnv.heroAnimation.src === "./squaremcp_launch_poster.png", "hero poster swap failed");
|
||||
|
||||
let prevented = false;
|
||||
await validEnv.form.dispatch("submit", {
|
||||
preventDefault() {
|
||||
prevented = true;
|
||||
},
|
||||
});
|
||||
assert(prevented, "submit did not prevent default");
|
||||
assert(validEnv.fetchCalls.length === 1, "submit did not call fetch");
|
||||
assert(validEnv.fetchCalls[0].url === "/api/pilot-request", "submit endpoint mismatch");
|
||||
assert(validEnv.fetchCalls[0].options.method === "POST", "submit method mismatch");
|
||||
const payload = JSON.parse(validEnv.fetchCalls[0].options.body);
|
||||
assert(payload.company === "SquareMCP Labs", "submit payload mismatch");
|
||||
assert(validEnv.form.resetCount === 1, "form did not reset after success");
|
||||
assert(validEnv.output.textContent.includes("Saved to SquareMCP intake successfully."), "submit success message missing");
|
||||
assert(validEnv.output.classList.has("ready"), "submit did not set ready class");
|
||||
|
||||
await validEnv.button.dispatch("click", {});
|
||||
assert(validEnv.getClipboardText().includes("SquareMCP Labs"), "copy handler did not write clipboard");
|
||||
assert(validEnv.output.textContent.includes("Copied to clipboard."), "copy success message missing");
|
||||
|
||||
const invalidEnv = createEnv({ valid: false });
|
||||
await invalidEnv.form.dispatch("submit", { preventDefault() {} });
|
||||
assert(invalidEnv.fetchCalls.length === 0, "invalid submit should not call fetch");
|
||||
assert(invalidEnv.output.textContent === "", "invalid submit should not update output");
|
||||
|
||||
const failureEnv = createEnv({ fetchOk: false });
|
||||
await failureEnv.form.dispatch("submit", { preventDefault() {} });
|
||||
assert(failureEnv.output.textContent.includes("Submission failed."), "submit failure message missing");
|
||||
assert(failureEnv.form.resetCount === 0, "failed submit should not reset form");
|
||||
|
||||
const clipboardFailureEnv = createEnv({ clipboardFails: true });
|
||||
await clipboardFailureEnv.button.dispatch("click", {});
|
||||
assert(
|
||||
clipboardFailureEnv.output.textContent.includes("Clipboard access failed."),
|
||||
"clipboard failure message missing"
|
||||
);
|
||||
|
||||
console.log("squaremcp product site smoke test: PASS");
|
||||
}
|
||||
|
||||
run().catch((error) => {
|
||||
console.error(`squaremcp product site smoke test: FAIL: ${error.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user