mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Merge pull request #975 from PR0M3TH3AN/codex/fix-comments-not-working-with-nip-07
Fix comment composer login check for nip07 logins
This commit is contained in:
5 files changed
+518
-83
No files matched your search
@@ -3494,13 +3494,32 @@ class Application {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sessionActorPubkey = this.normalizeHexPubkey(
|
||||
nostrClient?.sessionActor?.pubkey,
|
||||
);
|
||||
if (sessionActorPubkey && sessionActorPubkey === normalizedPubkey) {
|
||||
const clientPubkey = this.normalizeHexPubkey(nostrClient?.pubkey);
|
||||
if (clientPubkey && clientPubkey !== normalizedPubkey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sessionActor = nostrClient?.sessionActor || null;
|
||||
const sessionActorPubkey = this.normalizeHexPubkey(sessionActor?.pubkey);
|
||||
if (sessionActorPubkey && sessionActorPubkey !== normalizedPubkey) {
|
||||
const rawPrivateKey =
|
||||
typeof sessionActor?.privateKey === "string"
|
||||
? sessionActor.privateKey.trim()
|
||||
: "";
|
||||
const hasEmbeddedPrivateKey =
|
||||
rawPrivateKey.length > 0 && HEX64_REGEX.test(rawPrivateKey);
|
||||
|
||||
const declaredSource =
|
||||
typeof sessionActor?.source === "string"
|
||||
? sessionActor.source.trim()
|
||||
: "";
|
||||
const isPersisted = sessionActor?.persisted === true;
|
||||
|
||||
if (!hasEmbeddedPrivateKey || declaredSource || isPersisted) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3588,6 +3607,34 @@ class Application {
|
||||
}
|
||||
}
|
||||
|
||||
refreshChromeElements() {
|
||||
const doc = typeof document !== "undefined" ? document : null;
|
||||
const findElement = (id) => {
|
||||
if (!doc || typeof doc.getElementById !== "function") {
|
||||
return null;
|
||||
}
|
||||
const element = doc.getElementById(id);
|
||||
if (!element || typeof element.addEventListener !== "function") {
|
||||
return null;
|
||||
}
|
||||
return element;
|
||||
};
|
||||
|
||||
this.loginButton = findElement("loginButton");
|
||||
this.logoutButton = findElement("logoutButton");
|
||||
this.uploadButton = findElement("uploadButton");
|
||||
this.profileButton = findElement("profileButton");
|
||||
this.closeLoginModalBtn = findElement("closeLoginModal");
|
||||
|
||||
return {
|
||||
loginButton: this.loginButton,
|
||||
logoutButton: this.logoutButton,
|
||||
uploadButton: this.uploadButton,
|
||||
profileButton: this.profileButton,
|
||||
closeLoginModalButton: this.closeLoginModalBtn,
|
||||
};
|
||||
}
|
||||
|
||||
resolveSubscriptionsLink() {
|
||||
if (this.subscriptionsLink instanceof HTMLElement) {
|
||||
return this.subscriptionsLink;
|
||||
@@ -3604,7 +3651,20 @@ class Application {
|
||||
}
|
||||
|
||||
hydrateSidebarNavigation() {
|
||||
const chromeElements = this.refreshChromeElements();
|
||||
this.resolveSubscriptionsLink();
|
||||
|
||||
if (this.appChromeController) {
|
||||
if (typeof this.appChromeController.setElements === "function") {
|
||||
this.appChromeController.setElements(chromeElements);
|
||||
} else {
|
||||
if (this.appChromeController.elements) {
|
||||
Object.assign(this.appChromeController.elements, chromeElements);
|
||||
}
|
||||
this.appChromeController.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
this.syncAuthUiState();
|
||||
}
|
||||
|
||||
|
||||
+168
-79
@@ -16,11 +16,13 @@ export default class AppChromeController {
|
||||
logger,
|
||||
} = {}) {
|
||||
this.elements = {
|
||||
logoutButton: elements.logoutButton || null,
|
||||
profileButton: elements.profileButton || null,
|
||||
uploadButton: elements.uploadButton || null,
|
||||
loginButton: elements.loginButton || null,
|
||||
closeLoginModalButton: elements.closeLoginModalButton || null,
|
||||
logoutButton: this.normalizeElement(elements.logoutButton),
|
||||
profileButton: this.normalizeElement(elements.profileButton),
|
||||
uploadButton: this.normalizeElement(elements.uploadButton),
|
||||
loginButton: this.normalizeElement(elements.loginButton),
|
||||
closeLoginModalButton: this.normalizeElement(
|
||||
elements.closeLoginModalButton,
|
||||
),
|
||||
};
|
||||
|
||||
this.callbacks = {
|
||||
@@ -59,42 +61,17 @@ export default class AppChromeController {
|
||||
this.logger = logger || defaultLogger;
|
||||
|
||||
this.isInitialized = false;
|
||||
this.hasBoundGlobalEvents = false;
|
||||
|
||||
this.handleBeforeUnload = this.handleBeforeUnload.bind(this);
|
||||
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
|
||||
this.handlePopstate = this.handlePopstate.bind(this);
|
||||
this.handleDocumentClick = this.handleDocumentClick.bind(this);
|
||||
}
|
||||
this.boundElements = {
|
||||
logoutButton: null,
|
||||
profileButton: null,
|
||||
uploadButton: null,
|
||||
loginButton: null,
|
||||
closeLoginModalButton: null,
|
||||
};
|
||||
|
||||
initialize() {
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bindLogoutButton();
|
||||
this.bindProfileButton();
|
||||
this.bindUploadButton();
|
||||
this.bindLoginButton();
|
||||
this.bindCloseLoginButton();
|
||||
this.bindGlobalEvents();
|
||||
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
getTriggerFromEvent(event) {
|
||||
if (!event) {
|
||||
return null;
|
||||
}
|
||||
return event.currentTarget || event.target || null;
|
||||
}
|
||||
|
||||
bindLogoutButton() {
|
||||
const { logoutButton } = this.elements;
|
||||
if (!logoutButton || typeof logoutButton.addEventListener !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
logoutButton.addEventListener("click", async () => {
|
||||
this.handleLogoutClick = async () => {
|
||||
try {
|
||||
if (typeof this.callbacks.requestLogout === "function") {
|
||||
await this.callbacks.requestLogout();
|
||||
@@ -107,16 +84,9 @@ export default class AppChromeController {
|
||||
this.callbacks.showError(DEFAULT_LOGOUT_ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
bindProfileButton() {
|
||||
const { profileButton } = this.elements;
|
||||
if (!profileButton || typeof profileButton.addEventListener !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
profileButton.addEventListener("click", () => {
|
||||
this.handleProfileClick = () => {
|
||||
if (typeof this.callbacks.showProfileModal !== "function") {
|
||||
return;
|
||||
}
|
||||
@@ -128,19 +98,13 @@ export default class AppChromeController {
|
||||
this.logger.error("Failed to open profile modal:", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
bindUploadButton() {
|
||||
const { uploadButton } = this.elements;
|
||||
if (!uploadButton || typeof uploadButton.addEventListener !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
uploadButton.addEventListener("click", (event) => {
|
||||
this.handleUploadClick = (event) => {
|
||||
if (typeof this.callbacks.openUploadModal !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
const triggerElement = this.getTriggerFromEvent(event);
|
||||
try {
|
||||
this.callbacks.openUploadModal({ triggerElement });
|
||||
@@ -149,16 +113,9 @@ export default class AppChromeController {
|
||||
this.logger.error("Failed to open upload modal:", error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
bindLoginButton() {
|
||||
const { loginButton } = this.elements;
|
||||
if (!loginButton || typeof loginButton.addEventListener !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
loginButton.addEventListener("click", (event) => {
|
||||
this.handleLoginClick = (event) => {
|
||||
if (this.logger?.log) {
|
||||
this.logger.log("Login button clicked!");
|
||||
}
|
||||
@@ -178,19 +135,9 @@ export default class AppChromeController {
|
||||
this.callbacks.onLoginModalOpened();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
bindCloseLoginButton() {
|
||||
const { closeLoginModalButton } = this.elements;
|
||||
if (
|
||||
!closeLoginModalButton ||
|
||||
typeof closeLoginModalButton.addEventListener !== "function"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeLoginModalButton.addEventListener("click", () => {
|
||||
this.handleCloseLoginClick = () => {
|
||||
if (this.logger?.log) {
|
||||
this.logger.log("[app.js] closeLoginModal button clicked!");
|
||||
}
|
||||
@@ -201,7 +148,149 @@ export default class AppChromeController {
|
||||
this.callbacks.onLoginModalClosed();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.handleBeforeUnload = this.handleBeforeUnload.bind(this);
|
||||
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
|
||||
this.handlePopstate = this.handlePopstate.bind(this);
|
||||
this.handleDocumentClick = this.handleDocumentClick.bind(this);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.bindLogoutButton();
|
||||
this.bindProfileButton();
|
||||
this.bindUploadButton();
|
||||
this.bindLoginButton();
|
||||
this.bindCloseLoginButton();
|
||||
|
||||
if (!this.hasBoundGlobalEvents) {
|
||||
this.bindGlobalEvents();
|
||||
this.hasBoundGlobalEvents = true;
|
||||
}
|
||||
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
getTriggerFromEvent(event) {
|
||||
if (!event) {
|
||||
return null;
|
||||
}
|
||||
return event.currentTarget || event.target || null;
|
||||
}
|
||||
|
||||
bindLogoutButton() {
|
||||
const button = this.normalizeElement(this.elements.logoutButton);
|
||||
if (this.boundElements.logoutButton === button) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.detachBoundElement("logoutButton", this.handleLogoutClick);
|
||||
|
||||
if (button) {
|
||||
button.addEventListener("click", this.handleLogoutClick);
|
||||
this.boundElements.logoutButton = button;
|
||||
}
|
||||
}
|
||||
|
||||
bindProfileButton() {
|
||||
const button = this.normalizeElement(this.elements.profileButton);
|
||||
if (this.boundElements.profileButton === button) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.detachBoundElement("profileButton", this.handleProfileClick);
|
||||
|
||||
if (button) {
|
||||
button.addEventListener("click", this.handleProfileClick);
|
||||
this.boundElements.profileButton = button;
|
||||
}
|
||||
}
|
||||
|
||||
bindUploadButton() {
|
||||
const button = this.normalizeElement(this.elements.uploadButton);
|
||||
if (this.boundElements.uploadButton === button) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.detachBoundElement("uploadButton", this.handleUploadClick);
|
||||
|
||||
if (button) {
|
||||
button.addEventListener("click", this.handleUploadClick);
|
||||
this.boundElements.uploadButton = button;
|
||||
}
|
||||
}
|
||||
|
||||
bindLoginButton() {
|
||||
const button = this.normalizeElement(this.elements.loginButton);
|
||||
if (this.boundElements.loginButton === button) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.detachBoundElement("loginButton", this.handleLoginClick);
|
||||
|
||||
if (button) {
|
||||
button.addEventListener("click", this.handleLoginClick);
|
||||
this.boundElements.loginButton = button;
|
||||
}
|
||||
}
|
||||
|
||||
bindCloseLoginButton() {
|
||||
const button = this.normalizeElement(this.elements.closeLoginModalButton);
|
||||
if (this.boundElements.closeLoginModalButton === button) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.detachBoundElement("closeLoginModalButton", this.handleCloseLoginClick);
|
||||
|
||||
if (button) {
|
||||
button.addEventListener("click", this.handleCloseLoginClick);
|
||||
this.boundElements.closeLoginModalButton = button;
|
||||
}
|
||||
}
|
||||
|
||||
setElements(nextElements = {}) {
|
||||
if (!nextElements || typeof nextElements !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
const keys = [
|
||||
"logoutButton",
|
||||
"profileButton",
|
||||
"uploadButton",
|
||||
"loginButton",
|
||||
"closeLoginModalButton",
|
||||
];
|
||||
|
||||
let shouldRebind = false;
|
||||
|
||||
for (const key of keys) {
|
||||
if (Object.prototype.hasOwnProperty.call(nextElements, key)) {
|
||||
const normalized = this.normalizeElement(nextElements[key]);
|
||||
if (this.elements[key] !== normalized) {
|
||||
this.elements[key] = normalized;
|
||||
shouldRebind = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRebind) {
|
||||
this.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
detachBoundElement(key, handler) {
|
||||
const previous = this.boundElements[key];
|
||||
if (previous && typeof previous.removeEventListener === "function") {
|
||||
previous.removeEventListener("click", handler);
|
||||
}
|
||||
this.boundElements[key] = null;
|
||||
}
|
||||
|
||||
normalizeElement(element) {
|
||||
if (!element || typeof element.addEventListener !== "function") {
|
||||
return null;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
bindGlobalEvents() {
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
import { Application } from "../../js/app.js";
|
||||
|
||||
const VIEW_HTML = `<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div id="chrome">
|
||||
<button id="loginButton" class="btn"></button>
|
||||
<button id="logoutButton" class="hidden"></button>
|
||||
<button id="uploadButton" class="hidden" hidden></button>
|
||||
<button id="profileButton" class="hidden" hidden></button>
|
||||
<span id="userStatus" class="hidden"></span>
|
||||
<span id="userPubKey"></span>
|
||||
<button id="closeLoginModal"></button>
|
||||
</div>
|
||||
<a id="subscriptionsLink" class="hidden"></a>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
test("hydrateSidebarNavigation reveals chrome controls for authenticated viewers", () => {
|
||||
const dom = new JSDOM(VIEW_HTML, { url: "https://example.com" });
|
||||
const { window } = dom;
|
||||
|
||||
const previousWindow = global.window;
|
||||
const previousDocument = global.document;
|
||||
|
||||
global.window = window;
|
||||
global.document = window.document;
|
||||
const previousHTMLElement = global.HTMLElement;
|
||||
global.HTMLElement = window.HTMLElement;
|
||||
|
||||
try {
|
||||
const app = Object.create(Application.prototype);
|
||||
|
||||
app.appChromeController = {
|
||||
receivedElements: null,
|
||||
setElements(elements) {
|
||||
this.receivedElements = elements;
|
||||
},
|
||||
};
|
||||
|
||||
app.userStatus = window.document.getElementById("userStatus");
|
||||
app.userPubKey = window.document.getElementById("userPubKey");
|
||||
app.subscriptionsLink = null;
|
||||
app.isUserLoggedIn = () => true;
|
||||
app.log = () => {};
|
||||
app.showError = () => {};
|
||||
app.showSuccess = () => {};
|
||||
app.showStatus = () => {};
|
||||
|
||||
app.hydrateSidebarNavigation();
|
||||
|
||||
const uploadButton = window.document.getElementById("uploadButton");
|
||||
const profileButton = window.document.getElementById("profileButton");
|
||||
const loginButton = window.document.getElementById("loginButton");
|
||||
const logoutButton = window.document.getElementById("logoutButton");
|
||||
const subscriptionsLink = window.document.getElementById("subscriptionsLink");
|
||||
const closeLoginButton = window.document.getElementById("closeLoginModal");
|
||||
|
||||
assert.equal(app.uploadButton, uploadButton);
|
||||
assert.equal(app.profileButton, profileButton);
|
||||
assert.equal(app.loginButton, loginButton);
|
||||
assert.equal(app.logoutButton, logoutButton);
|
||||
assert.equal(app.closeLoginModalBtn, closeLoginButton);
|
||||
|
||||
assert.equal(uploadButton.classList.contains("hidden"), false);
|
||||
assert.equal(uploadButton.hasAttribute("hidden"), false);
|
||||
assert.equal(profileButton.classList.contains("hidden"), false);
|
||||
assert.equal(profileButton.hasAttribute("hidden"), false);
|
||||
assert.equal(loginButton.classList.contains("hidden"), true);
|
||||
assert.equal(loginButton.hasAttribute("hidden"), true);
|
||||
assert.equal(logoutButton.classList.contains("hidden"), false);
|
||||
assert.equal(subscriptionsLink.classList.contains("hidden"), false);
|
||||
|
||||
assert.ok(app.appChromeController.receivedElements);
|
||||
assert.equal(
|
||||
app.appChromeController.receivedElements.uploadButton,
|
||||
uploadButton,
|
||||
);
|
||||
assert.equal(
|
||||
app.appChromeController.receivedElements.profileButton,
|
||||
profileButton,
|
||||
);
|
||||
} finally {
|
||||
global.window = previousWindow;
|
||||
global.document = previousDocument;
|
||||
if (previousHTMLElement === undefined) {
|
||||
delete global.HTMLElement;
|
||||
} else {
|
||||
global.HTMLElement = previousHTMLElement;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { Application } from "../../js/app.js";
|
||||
import { nostrClient } from "../../js/nostrClientFacade.js";
|
||||
|
||||
const HEX64_REGEX = /^[0-9a-f]{64}$/i;
|
||||
|
||||
function createTestApp(pubkey) {
|
||||
const app = {
|
||||
_pubkey: pubkey,
|
||||
normalizeHexPubkey(value) {
|
||||
if (typeof value !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (HEX64_REGEX.test(trimmed)) {
|
||||
return trimmed.toLowerCase();
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
isUserLoggedIn: Application.prototype.isUserLoggedIn,
|
||||
};
|
||||
|
||||
Object.defineProperty(app, "pubkey", {
|
||||
get() {
|
||||
return this._pubkey;
|
||||
},
|
||||
set(value) {
|
||||
this._pubkey = value;
|
||||
},
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
test("isUserLoggedIn returns false when no user pubkey is set", () => {
|
||||
const previousPubkey = nostrClient.pubkey;
|
||||
const previousSessionActor = nostrClient.sessionActor;
|
||||
|
||||
try {
|
||||
nostrClient.pubkey = null;
|
||||
nostrClient.sessionActor = null;
|
||||
|
||||
const app = createTestApp(null);
|
||||
assert.equal(app.isUserLoggedIn(), false);
|
||||
} finally {
|
||||
nostrClient.pubkey = previousPubkey;
|
||||
nostrClient.sessionActor = previousSessionActor;
|
||||
}
|
||||
});
|
||||
|
||||
test("isUserLoggedIn treats extension logins as authenticated", () => {
|
||||
const previousPubkey = nostrClient.pubkey;
|
||||
const previousSessionActor = nostrClient.sessionActor;
|
||||
|
||||
try {
|
||||
const pubkey = "893a31fa5a6481295bb76ea2dd8d5ec70640e6e6ad398e6547d771e8256af11f";
|
||||
nostrClient.pubkey = pubkey;
|
||||
nostrClient.sessionActor = { pubkey };
|
||||
|
||||
const app = createTestApp(pubkey);
|
||||
assert.equal(app.isUserLoggedIn(), true);
|
||||
} finally {
|
||||
nostrClient.pubkey = previousPubkey;
|
||||
nostrClient.sessionActor = previousSessionActor;
|
||||
}
|
||||
});
|
||||
|
||||
test("isUserLoggedIn guards against mismatched nostrClient state", () => {
|
||||
const previousPubkey = nostrClient.pubkey;
|
||||
const previousSessionActor = nostrClient.sessionActor;
|
||||
|
||||
try {
|
||||
const pubkey = "a".repeat(64);
|
||||
nostrClient.pubkey = "b".repeat(64);
|
||||
nostrClient.sessionActor = { pubkey };
|
||||
|
||||
const app = createTestApp(pubkey);
|
||||
assert.equal(app.isUserLoggedIn(), false);
|
||||
} finally {
|
||||
nostrClient.pubkey = previousPubkey;
|
||||
nostrClient.sessionActor = previousSessionActor;
|
||||
}
|
||||
});
|
||||
|
||||
test("isUserLoggedIn ignores anonymous session actor mismatches", () => {
|
||||
const previousPubkey = nostrClient.pubkey;
|
||||
const previousSessionActor = nostrClient.sessionActor;
|
||||
|
||||
try {
|
||||
const pubkey = "c".repeat(64);
|
||||
nostrClient.pubkey = pubkey;
|
||||
nostrClient.sessionActor = {
|
||||
pubkey: "d".repeat(64),
|
||||
privateKey: "e".repeat(64),
|
||||
createdAt: Date.now(),
|
||||
};
|
||||
|
||||
const app = createTestApp(pubkey);
|
||||
assert.equal(app.isUserLoggedIn(), true);
|
||||
} finally {
|
||||
nostrClient.pubkey = previousPubkey;
|
||||
nostrClient.sessionActor = previousSessionActor;
|
||||
}
|
||||
});
|
||||
|
||||
test("isUserLoggedIn rejects mismatched managed session actors", () => {
|
||||
const previousPubkey = nostrClient.pubkey;
|
||||
const previousSessionActor = nostrClient.sessionActor;
|
||||
|
||||
try {
|
||||
const pubkey = "f".repeat(64);
|
||||
nostrClient.pubkey = pubkey;
|
||||
nostrClient.sessionActor = {
|
||||
pubkey: "0".repeat(64),
|
||||
source: "nsec",
|
||||
};
|
||||
|
||||
const app = createTestApp(pubkey);
|
||||
assert.equal(app.isUserLoggedIn(), false);
|
||||
} finally {
|
||||
nostrClient.pubkey = previousPubkey;
|
||||
nostrClient.sessionActor = previousSessionActor;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
import AppChromeController from "../../js/ui/appChromeController.js";
|
||||
|
||||
test("AppChromeController binds upload button when elements hydrate later", () => {
|
||||
const dom = new JSDOM(`<!DOCTYPE html><body><button id="upload"></button></body>`, {
|
||||
url: "https://example.com",
|
||||
});
|
||||
const { window } = dom;
|
||||
|
||||
const previousWindow = global.window;
|
||||
const previousDocument = global.document;
|
||||
|
||||
global.window = window;
|
||||
global.document = window.document;
|
||||
|
||||
try {
|
||||
const openCalls = [];
|
||||
const controller = new AppChromeController({
|
||||
callbacks: {
|
||||
openUploadModal: (detail) => {
|
||||
openCalls.push(detail);
|
||||
},
|
||||
},
|
||||
logger: {
|
||||
error: () => {},
|
||||
log: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
controller.initialize();
|
||||
|
||||
const uploadButton = window.document.getElementById("upload");
|
||||
controller.setElements({ uploadButton });
|
||||
|
||||
uploadButton.dispatchEvent(new window.Event("click", { bubbles: true }));
|
||||
|
||||
assert.equal(openCalls.length, 1);
|
||||
assert.equal(openCalls[0]?.triggerElement, uploadButton);
|
||||
|
||||
const replacement = window.document.createElement("button");
|
||||
window.document.body.appendChild(replacement);
|
||||
|
||||
controller.setElements({ uploadButton: replacement });
|
||||
|
||||
uploadButton.dispatchEvent(new window.Event("click", { bubbles: true }));
|
||||
assert.equal(openCalls.length, 1);
|
||||
|
||||
replacement.dispatchEvent(new window.Event("click", { bubbles: true }));
|
||||
assert.equal(openCalls.length, 2);
|
||||
assert.equal(openCalls[1]?.triggerElement, replacement);
|
||||
} finally {
|
||||
global.window = previousWindow;
|
||||
global.document = previousDocument;
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user