Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ config.transformIgnorePatterns = [
// tight and fails sporadically. Give all tests a bit more room.
config.testTimeout = 10000;

// add svelte-jester
config.transform["^.+\\.svelte$"] = "svelte-jester";
// svelte-jester refuses to run in Jest's CJS mode, so use a custom client
// compile + ESM->CJS transformer (see the tool for the rationale).
config.transform["^.+\\.svelte$"] = path.resolve(__dirname, "./tools/jest-svelte-component.cjs");

// console.log(JSON.stringify(config, null, 4));
module.exports = config;
69 changes: 69 additions & 0 deletions src/pat/contentbrowser/contentbrowser-selection.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import "./contentbrowser";
import registry from "@patternslib/patternslib/src/core/registry";
import utils from "@patternslib/patternslib/src/core/utils";

const vocabularyUrl = "http://localhost/plone/@@getVocabulary";

const item = (uid, title) => ({
UID: uid,
Title: title,
"@id": `http://localhost/plone/${uid}`,
path: `/plone/${uid}`,
portal_type: "Document",
getIcon: "",
is_folderish: false,
review_state: "published",
getURL: `http://localhost/plone/${uid}`,
});

function mockVocabulary(items) {
global.fetch = jest.fn(async () => ({
ok: true,
json: async () => ({ results: items, total: items.length }),
}));
}

async function mountField(value) {
const options = { vocabularyUrl };
document.body.innerHTML = `
<div id="contentbrowser-field">
<input type="text" id="field" value="${value}" class="pat-contentbrowser"
data-pat-contentbrowser='${JSON.stringify(options)}'>
</div>
`;
const input = document.getElementById("field");
const changes = [];
input.addEventListener("change", () => changes.push(input.value));
registry.scan(document.body);
await utils.timeout(1);
return { input, changes };
}

describe("Content Browser selection", () => {
afterEach(() => {
document.body.innerHTML = "";
delete global.fetch;
});

it("does not fire change while rendering the initial selection", async () => {
mockVocabulary([item("uid-1", "One"), item("uid-2", "Two")]);
const { input, changes } = await mountField("uid-1,uid-2");
await utils.timeout(1);

expect(document.querySelectorAll(".selected-item").length).toEqual(2);
expect(input.value).toEqual("uid-1,uid-2");
expect(changes).toEqual([]);
});

it("fires change when an item is unselected", async () => {
mockVocabulary([item("uid-1", "One"), item("uid-2", "Two")]);
const { input, changes } = await mountField("uid-1,uid-2");
await utils.timeout(1);

document.querySelector(".selected-item button[aria-label=remove]").click();
await utils.timeout(1);

expect(input.value).toEqual("uid-2");
expect(changes).toEqual(["uid-2"]);
});
});
6 changes: 6 additions & 0 deletions src/pat/contentbrowser/src/SelectedItems.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@

function setNodeValue(selectedUids) {
const node_val = selectedUids.join($config.separator);
if (selectedItemsNode.value === node_val) {
// Nothing changed — the initial selection being written back
// on mount must not look like an edit (pat-formunloadalert
// would otherwise warn on every page leave).
return;
}
selectedItemsNode.value = node_val;
selectedItemsNode.dispatchEvent(events.change_event());
}
Expand Down
38 changes: 38 additions & 0 deletions tools/jest-svelte-component.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Jest transformer for Svelte 5 components (.svelte).
//
// svelte-jester refuses to run while Jest is in CJS mode, which is the mode the
// Patternslib base jest config uses — so component-mount tests are otherwise
// impossible here. Compile the component to client JS with the svelte compiler
// and down-convert the emitted ESM to CommonJS so Jest's default runtime can
// require it. Backported from master (tools/jest-svelte-component.cjs).
const babel = require("@babel/core");
const svelte = require("svelte/compiler");

module.exports = {
process(source, filename) {
const compiled = svelte.compile(source, {
filename,
dev: true,
generate: "client",
}).js.code;

const cjs = babel.transformSync(compiled, {
filename,
babelrc: false,
configFile: false,
sourceMaps: "inline",
plugins: ["@babel/plugin-transform-modules-commonjs"],
});

return { code: cjs.code };
},

getCacheKey(source, filename) {
return require("crypto")
.createHash("md5")
.update(source)
.update(filename)
.update("svelte-component-v1")
.digest("hex");
},
};
Loading