Module API for adding new file upload mechanisms (#33355)
* Initial reword of upload to MVVM. * Update tests * More incremental improvements * Refactor tests to use helper method for composer uploads. * Add drag and drop tests * lint * Add commentary * fixup test * More precise selector * Retarget uploads * lint * fixup * one more type * update snap * Fixup composerUploadFiles * fix import * lint * Copy and paste fixes too * Add tests for pasting * Add tests for pasting files. * Remove redundant fn * rm comment * tidy up * Test cleanup * More clean up * another fix * Begin fleshing out * Park changes * More stuff * Use condensed version * Cleanup tests * more cleaning * last bity * Add a test for the composer * Park up changes * Rewrite Measured to be a functional component * Add tests to cover narrow viewports * lint * breakpoint is optional * Cleanup * Support narrow mode * fixup * begone * Provide default value * add label * fixup test * update copyright * cleanup * Be a bit more lazy with FileDropTarget * remove a debug statement * Fixup * fix two snaps * Update screenshot * and the other one * Update snaps * unfake CIDER * update screens again * remove extra test * Undo accidental snapshots * Bit of tidyup * fixup * even more tidyup * may drag and drop file * tidy up again * snap snap snap * Use load to make sonarQube happy * Bunch of refactors * More cleanup * cleanup debug code * tweaks * remove a test we no longer need * make it happy * fix import * fixup * Update snaps * typo * one off * Add tests * lint * remove only * Reduce screenshot scope * fix snapshot usage * cleanup
This commit is contained in:
@@ -10,26 +10,21 @@ import { render, fireEvent } from "jest-matrix-react";
|
||||
import { useMockedViewModel } from "@element-hq/web-shared-components";
|
||||
|
||||
import FileDropTarget from "../../../../src/components/structures/FileDropTarget.tsx";
|
||||
import {
|
||||
RoomUploadContext,
|
||||
type RoomUploadViewActions,
|
||||
type RoomUploadViewModel,
|
||||
type RoomUploadViewSnapshot,
|
||||
} from "../../../../src/viewmodels/room/RoomUploadViewModel.tsx";
|
||||
import { RoomUploadContext, type RoomUploadViewModel } from "../../../../src/viewmodels/room/RoomUploadViewModel.tsx";
|
||||
|
||||
function FileDropTargetWrapped({
|
||||
element,
|
||||
snapshot,
|
||||
actions,
|
||||
mayDragAndDropFile = false,
|
||||
onFileDrop,
|
||||
}: {
|
||||
element: HTMLDivElement;
|
||||
snapshot: RoomUploadViewSnapshot;
|
||||
actions: Partial<RoomUploadViewActions>;
|
||||
mayDragAndDropFile?: boolean;
|
||||
onFileDrop: RoomUploadViewModel["initiateViaDataTransfer"];
|
||||
}) {
|
||||
const mockVm = useMockedViewModel<RoomUploadViewSnapshot, RoomUploadViewActions>(
|
||||
snapshot,
|
||||
actions as RoomUploadViewActions,
|
||||
);
|
||||
const mockVm = useMockedViewModel<
|
||||
{ mayDragAndDropFile: boolean },
|
||||
Pick<RoomUploadViewModel, "initiateViaDataTransfer">
|
||||
>({ mayDragAndDropFile }, { initiateViaDataTransfer: onFileDrop });
|
||||
return (
|
||||
<RoomUploadContext.Provider value={mockVm as RoomUploadViewModel}>
|
||||
<FileDropTarget parent={element} />
|
||||
@@ -43,11 +38,7 @@ describe("FileDropTarget", () => {
|
||||
const onFileDrop = jest.fn();
|
||||
|
||||
const { asFragment } = render(
|
||||
<FileDropTargetWrapped
|
||||
element={element}
|
||||
snapshot={{ mayUpload: true }}
|
||||
actions={{ initiateViaDataTransfer: onFileDrop }}
|
||||
/>,
|
||||
<FileDropTargetWrapped element={element} mayDragAndDropFile onFileDrop={onFileDrop} />,
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
@@ -56,11 +47,7 @@ describe("FileDropTarget", () => {
|
||||
const element = document.createElement("div");
|
||||
const onFileDrop = jest.fn();
|
||||
const { asFragment } = render(
|
||||
<FileDropTargetWrapped
|
||||
element={element}
|
||||
snapshot={{ mayUpload: true }}
|
||||
actions={{ initiateViaDataTransfer: onFileDrop }}
|
||||
/>,
|
||||
<FileDropTargetWrapped element={element} mayDragAndDropFile onFileDrop={onFileDrop} />,
|
||||
);
|
||||
fireEvent.dragEnter(element, {
|
||||
dataTransfer: {
|
||||
@@ -73,13 +60,7 @@ describe("FileDropTarget", () => {
|
||||
it("should not render drop file prompt on mouse over with file if permissions do not allow", () => {
|
||||
const element = document.createElement("div");
|
||||
const onFileDrop = jest.fn();
|
||||
const { asFragment } = render(
|
||||
<FileDropTargetWrapped
|
||||
element={element}
|
||||
snapshot={{ mayUpload: false }}
|
||||
actions={{ initiateViaDataTransfer: onFileDrop }}
|
||||
/>,
|
||||
);
|
||||
const { asFragment } = render(<FileDropTargetWrapped element={element} onFileDrop={onFileDrop} />);
|
||||
fireEvent.dragEnter(element, {
|
||||
dataTransfer: {
|
||||
types: ["Files"],
|
||||
|
||||
@@ -1079,27 +1079,6 @@ describe("RoomView", () => {
|
||||
});
|
||||
|
||||
describe("handles Action.ComposerInsert", () => {
|
||||
it("redispatches an empty composerType, timelineRenderingType with the current state", async () => {
|
||||
await mountRoomView();
|
||||
const promise = untilDispatch((payload) => {
|
||||
try {
|
||||
expect(payload).toEqual({
|
||||
action: Action.ComposerInsert,
|
||||
text: "Hello world",
|
||||
timelineRenderingType: TimelineRenderingType.Room,
|
||||
composerType: ComposerType.Send,
|
||||
});
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, defaultDispatcher);
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ComposerInsert,
|
||||
text: "Hello world",
|
||||
} satisfies ComposerInsertPayload);
|
||||
await promise;
|
||||
});
|
||||
it("redispatches an empty composerType with the current state", async () => {
|
||||
await mountRoomView();
|
||||
const promise = untilDispatch((payload) => {
|
||||
|
||||
+181
-125
@@ -212,7 +212,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-labelledby="_r_1cl_"
|
||||
aria-labelledby="_r_1dt_"
|
||||
class="_banner_n7ud0_8"
|
||||
data-type="critical"
|
||||
role="status"
|
||||
@@ -238,7 +238,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||
>
|
||||
<p
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _title_mcq5y_24"
|
||||
id="_r_1cl_"
|
||||
id="_r_1dt_"
|
||||
>
|
||||
Could not start a chat with this user
|
||||
</p>
|
||||
@@ -423,7 +423,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||
>
|
||||
<svg
|
||||
aria-label="Messages in this room are not end-to-end encrypted"
|
||||
aria-labelledby="_r_19j_"
|
||||
aria-labelledby="_r_1an_"
|
||||
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
|
||||
color="var(--cpd-color-icon-info-primary)"
|
||||
fill="currentColor"
|
||||
@@ -608,24 +608,32 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
<button
|
||||
aria-label="Attachment"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button"
|
||||
aria-labelledby="_r_1bs_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 26px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
aria-label="More options"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||
@@ -702,7 +710,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
</span>
|
||||
<svg
|
||||
aria-label="New members see history"
|
||||
aria-labelledby="_r_1cf_"
|
||||
aria-labelledby="_r_1dn_"
|
||||
class="mx_RoomHeader_icon"
|
||||
color="var(--cpd-color-icon-info-primary)"
|
||||
fill="currentColor"
|
||||
@@ -981,24 +989,32 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
<button
|
||||
aria-label="Attachment"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button"
|
||||
aria-labelledby="_r_1dc_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 26px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
aria-label="More options"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||
@@ -1599,24 +1615,32 @@ exports[`RoomView should hide the header when hideHeader=true 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
<button
|
||||
aria-label="Attachment"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button"
|
||||
aria-labelledby="_r_27_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 26px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
aria-label="More options"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||
@@ -1724,7 +1748,7 @@ exports[`RoomView should hide the pinned message banner when hidePinnedMessageBa
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="_r_5e_"
|
||||
aria-labelledby="_r_5i_"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -1751,7 +1775,7 @@ exports[`RoomView should hide the pinned message banner when hidePinnedMessageBa
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="_r_5j_"
|
||||
aria-labelledby="_r_5n_"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -1766,7 +1790,7 @@ exports[`RoomView should hide the pinned message banner when hidePinnedMessageBa
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby="_r_5o_"
|
||||
aria-labelledby="_r_5s_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -1793,7 +1817,7 @@ exports[`RoomView should hide the pinned message banner when hidePinnedMessageBa
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby="_r_5t_"
|
||||
aria-labelledby="_r_61_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -1823,7 +1847,7 @@ exports[`RoomView should hide the pinned message banner when hidePinnedMessageBa
|
||||
>
|
||||
<div
|
||||
aria-label="0 members"
|
||||
aria-labelledby="_r_62_"
|
||||
aria-labelledby="_r_66_"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -1890,7 +1914,7 @@ exports[`RoomView should hide the pinned message banner when hidePinnedMessageBa
|
||||
>
|
||||
<svg
|
||||
aria-label="Messages in this room are not end-to-end encrypted"
|
||||
aria-labelledby="_r_6e_"
|
||||
aria-labelledby="_r_6i_"
|
||||
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
|
||||
color="var(--cpd-color-icon-info-primary)"
|
||||
fill="currentColor"
|
||||
@@ -2075,24 +2099,32 @@ exports[`RoomView should hide the pinned message banner when hidePinnedMessageBa
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
<button
|
||||
aria-label="Attachment"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button"
|
||||
aria-labelledby="_r_7n_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 26px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
aria-label="More options"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||
@@ -2200,7 +2232,7 @@ exports[`RoomView should hide the right panel when hideRightPanel=true 1`] = `
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="_r_2g_"
|
||||
aria-labelledby="_r_2i_"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -2227,7 +2259,7 @@ exports[`RoomView should hide the right panel when hideRightPanel=true 1`] = `
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="_r_2l_"
|
||||
aria-labelledby="_r_2n_"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -2242,7 +2274,7 @@ exports[`RoomView should hide the right panel when hideRightPanel=true 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby="_r_2q_"
|
||||
aria-labelledby="_r_2s_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -2269,7 +2301,7 @@ exports[`RoomView should hide the right panel when hideRightPanel=true 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby="_r_2v_"
|
||||
aria-labelledby="_r_31_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -2299,7 +2331,7 @@ exports[`RoomView should hide the right panel when hideRightPanel=true 1`] = `
|
||||
>
|
||||
<div
|
||||
aria-label="0 members"
|
||||
aria-labelledby="_r_34_"
|
||||
aria-labelledby="_r_36_"
|
||||
class="mx_AccessibleButton mx_FacePile mx_FacePile_toggled"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -2366,7 +2398,7 @@ exports[`RoomView should hide the right panel when hideRightPanel=true 1`] = `
|
||||
>
|
||||
<svg
|
||||
aria-label="Messages in this room are not end-to-end encrypted"
|
||||
aria-labelledby="_r_3g_"
|
||||
aria-labelledby="_r_3i_"
|
||||
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
|
||||
color="var(--cpd-color-icon-info-primary)"
|
||||
fill="currentColor"
|
||||
@@ -2551,24 +2583,32 @@ exports[`RoomView should hide the right panel when hideRightPanel=true 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
<button
|
||||
aria-label="Attachment"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button"
|
||||
aria-labelledby="_r_4n_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 26px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
aria-label="More options"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||
@@ -2676,7 +2716,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="_r_fc_"
|
||||
aria-labelledby="_r_fo_"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -2703,7 +2743,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="_r_fh_"
|
||||
aria-labelledby="_r_ft_"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -2718,7 +2758,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby="_r_fm_"
|
||||
aria-labelledby="_r_g2_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -2745,7 +2785,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby="_r_fr_"
|
||||
aria-labelledby="_r_g7_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -2775,7 +2815,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
>
|
||||
<div
|
||||
aria-label="0 members"
|
||||
aria-labelledby="_r_g0_"
|
||||
aria-labelledby="_r_gc_"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -2881,7 +2921,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
</span>
|
||||
<svg
|
||||
aria-label="New members see history"
|
||||
aria-labelledby="_r_gc_"
|
||||
aria-labelledby="_r_go_"
|
||||
class="mx_RoomHeader_icon"
|
||||
color="var(--cpd-color-icon-info-primary)"
|
||||
fill="currentColor"
|
||||
@@ -2914,7 +2954,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="_r_fc_"
|
||||
aria-labelledby="_r_fo_"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -2941,7 +2981,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby="_r_fh_"
|
||||
aria-labelledby="_r_ft_"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -2956,7 +2996,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby="_r_fm_"
|
||||
aria-labelledby="_r_g2_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -2983,7 +3023,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby="_r_fr_"
|
||||
aria-labelledby="_r_g7_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -3013,7 +3053,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
>
|
||||
<div
|
||||
aria-label="0 members"
|
||||
aria-labelledby="_r_g0_"
|
||||
aria-labelledby="_r_gc_"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -3082,7 +3122,7 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="_r_gh_"
|
||||
aria-labelledby="_r_gt_"
|
||||
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
|
||||
data-testid="e2e-icon"
|
||||
style="width: 12px; height: 12px;"
|
||||
@@ -3275,24 +3315,32 @@ exports[`RoomView should not display the timeline when the room encryption is lo
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
<button
|
||||
aria-label="Attachment"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button"
|
||||
aria-labelledby="_r_i2_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 26px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
aria-label="More options"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||
@@ -3418,7 +3466,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Chat"
|
||||
aria-labelledby="_r_ks_"
|
||||
aria-labelledby="_r_lc_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -3445,7 +3493,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby="_r_l1_"
|
||||
aria-labelledby="_r_lh_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -3472,7 +3520,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby="_r_l6_"
|
||||
aria-labelledby="_r_lm_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
@@ -3502,7 +3550,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
>
|
||||
<div
|
||||
aria-label="0 members"
|
||||
aria-labelledby="_r_lb_"
|
||||
aria-labelledby="_r_lr_"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -3587,7 +3635,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby="_r_lk_"
|
||||
aria-labelledby="_r_m4_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="secondary"
|
||||
data-testid="base-card-close-button"
|
||||
@@ -3647,7 +3695,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
>
|
||||
<svg
|
||||
aria-label="Messages in this room are not end-to-end encrypted"
|
||||
aria-labelledby="_r_lp_"
|
||||
aria-labelledby="_r_m9_"
|
||||
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
|
||||
color="var(--cpd-color-icon-info-primary)"
|
||||
fill="currentColor"
|
||||
@@ -3832,24 +3880,32 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
<button
|
||||
aria-label="Attachment"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button"
|
||||
aria-labelledby="_r_ne_"
|
||||
class="_icon-button_1215g_8"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 26px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5 22q-2.3 0-3.9-1.6T6 16.5V6q0-1.65 1.175-2.825T10 2t2.825 1.175T14 6v9.5q0 1.05-.725 1.775T11.5 18t-1.775-.725T9 15.5V6.75A.73.73 0 0 1 9.75 6a.73.73 0 0 1 .75.75v8.75q0 .424.287.712.288.288.713.288.424 0 .713-.288a.97.97 0 0 0 .287-.712V6q0-1.05-.725-1.775T10 3.5t-1.775.725T7.5 6v10.5q0 1.65 1.175 2.825T11.5 20.5t2.825-1.175T15.5 16.5V6.75a.73.73 0 0 1 .75-.75.73.73 0 0 1 .75.75v9.75q0 2.3-1.6 3.9T11.5 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
aria-label="More options"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||
|
||||
@@ -35,11 +35,8 @@ import { addTextToComposerRTL } from "../../../../test-utils/composer";
|
||||
import UIStore, { UI_EVENTS } from "../../../../../src/stores/UIStore";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
import type { RoomContextType } from "../../../../../src/contexts/RoomContext.ts";
|
||||
import {
|
||||
RoomUploadContext,
|
||||
type RoomUploadViewModel,
|
||||
} from "../../../../../src/viewmodels/room/RoomUploadViewModel.tsx";
|
||||
import { TimelineRenderingType, type RoomContextType } from "../../../../../src/contexts/RoomContext.ts";
|
||||
import { RoomUploadContextProvider } from "../../../../../src/viewmodels/room/RoomUploadViewModel.tsx";
|
||||
|
||||
const openStickerPicker = async (): Promise<void> => {
|
||||
await userEvent.click(screen.getByLabelText("More options"));
|
||||
@@ -462,7 +459,8 @@ function wrapAndRender(
|
||||
canSendMessages,
|
||||
tombstone,
|
||||
narrow,
|
||||
} as unknown as RoomContextType;
|
||||
timelineRenderingType: TimelineRenderingType.Room,
|
||||
} satisfies Partial<RoomContextType> as RoomContextType;
|
||||
|
||||
const defaultProps = {
|
||||
room,
|
||||
@@ -473,9 +471,9 @@ function wrapAndRender(
|
||||
const getRawComponent = (props = {}, context = roomContext, client = mockClient) => (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<ScopedRoomContextProvider {...context}>
|
||||
<RoomUploadContext.Provider value={{} as RoomUploadViewModel}>
|
||||
<RoomUploadContextProvider>
|
||||
<MessageComposer {...defaultProps} {...props} />
|
||||
</RoomUploadContext.Provider>
|
||||
</RoomUploadContextProvider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
@@ -5,20 +5,79 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { TimelineRenderingType } from "../../../src/contexts/RoomContext";
|
||||
import { Action } from "../../../src/dispatcher/actions";
|
||||
import type { MatrixDispatcher } from "../../../src/dispatcher/dispatcher";
|
||||
import { ComposerApi } from "../../../src/modules/ComposerApi";
|
||||
import { MatrixDispatcher } from "../../../src/dispatcher/dispatcher";
|
||||
import { type ComposerInsertPayload, ComposerType } from "../../../src/dispatcher/payloads/ComposerInsertPayload";
|
||||
import { ComposerApi, ModuleComposerApiEvents } from "../../../src/modules/ComposerApi";
|
||||
import type { ComposerInsertFilesPayload } from "../../../src/dispatcher/payloads/ComposerInsertFilePayload";
|
||||
|
||||
describe("ComposerApi", () => {
|
||||
it("should be able to insert text via insertTextIntoComposer()", () => {
|
||||
const dispatcher = {
|
||||
dispatch: jest.fn(),
|
||||
} as unknown as MatrixDispatcher;
|
||||
const api = new ComposerApi(dispatcher);
|
||||
api.insertPlaintextIntoComposer("Hello world");
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: Action.ComposerInsert,
|
||||
text: "Hello world",
|
||||
describe("insertPlaintextIntoComposer()", () => {
|
||||
it("should be able to insert text", () => {
|
||||
const dispatcher = {
|
||||
dispatch: jest.fn(),
|
||||
} as unknown as MatrixDispatcher;
|
||||
const api = new ComposerApi(dispatcher);
|
||||
api.insertPlaintextIntoComposer("Hello world", { view: "room" });
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: Action.ComposerInsert,
|
||||
text: "Hello world",
|
||||
timelineRenderingType: TimelineRenderingType.Room,
|
||||
composerType: ComposerType.Send,
|
||||
} satisfies ComposerInsertPayload);
|
||||
});
|
||||
it("throws if called with an invalid view", () => {
|
||||
const api = new ComposerApi(new MatrixDispatcher());
|
||||
expect(() => api.insertPlaintextIntoComposer("text", { view: "bleh" as "room" })).toThrow(
|
||||
"Invalid view 'bleh'",
|
||||
);
|
||||
});
|
||||
});
|
||||
describe("openFileUploadConfirmation()", () => {
|
||||
it("should be able to initiate a file upload", () => {
|
||||
const dispatcher = {
|
||||
dispatch: jest.fn(),
|
||||
} as unknown as MatrixDispatcher;
|
||||
const api = new ComposerApi(dispatcher);
|
||||
const files = [new File(["test"], "test.txt")];
|
||||
api.openFileUploadConfirmation(files, { view: "room" });
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: Action.ComposerFileInsert,
|
||||
files: files,
|
||||
timelineRenderingType: TimelineRenderingType.Room,
|
||||
} satisfies ComposerInsertFilesPayload);
|
||||
});
|
||||
it("throws if called with an invalid view", () => {
|
||||
const api = new ComposerApi(new MatrixDispatcher());
|
||||
const files = [new File(["test"], "test.txt")];
|
||||
expect(() => api.openFileUploadConfirmation(files, { view: "bleh" as "room" })).toThrow(
|
||||
"Invalid view 'bleh'",
|
||||
);
|
||||
});
|
||||
});
|
||||
describe("addFileUploadOption()", () => {
|
||||
it("should be able to add a file upload option", () => {
|
||||
const api = new ComposerApi(new MatrixDispatcher());
|
||||
const eventCb = jest.fn();
|
||||
api.on(ModuleComposerApiEvents.UploaderOptionsChanged, eventCb);
|
||||
const option = { type: "an_option", label: "New option", onSelected: () => {} };
|
||||
api.addFileUploadOption(option);
|
||||
expect(api.fileUploadOptions).toHaveLength(1);
|
||||
expect(api.fileUploadOptions).toContain(option);
|
||||
expect(eventCb).toHaveBeenCalledWith(option);
|
||||
});
|
||||
it("throws if called with the type 'local'", () => {
|
||||
const api = new ComposerApi(new MatrixDispatcher());
|
||||
expect(() => api.addFileUploadOption({ type: "local", label: "New option", onSelected: () => {} })).toThrow(
|
||||
'Option "local" is reserved',
|
||||
);
|
||||
});
|
||||
it("throws if called with a duplicate type", () => {
|
||||
const api = new ComposerApi(new MatrixDispatcher());
|
||||
const option = { type: "an_option", label: "New option", onSelected: () => {} };
|
||||
api.addFileUploadOption(option);
|
||||
expect(() => api.addFileUploadOption({ ...option })).toThrow('Option "an_option" already exists');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+86
-7
@@ -4,15 +4,21 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { type IEventRelation, type MatrixClient, type Room, RoomEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { render } from "jest-matrix-react";
|
||||
|
||||
import type { MockedObject } from "jest-mock";
|
||||
import { RoomUploadViewModel } from "../../../src/viewmodels/room/RoomUploadViewModel";
|
||||
import { mkEvent, mkStubRoom, stubClient } from "../../test-utils";
|
||||
import { RoomUploadContextProvider, RoomUploadViewModel } from "../../../src/viewmodels/room/RoomUploadViewModel";
|
||||
import { getRoomContext, mkEvent, mkStubRoom, stubClient } from "../../test-utils";
|
||||
import { TimelineRenderingType } from "../../../src/contexts/RoomContext";
|
||||
import type { MatrixDispatcher } from "../../../src/dispatcher/dispatcher";
|
||||
import defaultDispatcher, { MatrixDispatcher } from "../../../src/dispatcher/dispatcher";
|
||||
import ContentMessages from "../../../src/ContentMessages";
|
||||
import { ComposerApi } from "../../../src/modules/ComposerApi";
|
||||
import type { ComposerInsertFilesPayload } from "../../../src/dispatcher/payloads/ComposerInsertFilePayload";
|
||||
import { ScopedRoomContextProvider } from "../../../src/contexts/ScopedRoomContext";
|
||||
import { Action } from "../../../src/dispatcher/actions";
|
||||
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
||||
const sendContentListToRoomSpy = jest.spyOn(ContentMessages.sharedInstance(), "sendContentListToRoom");
|
||||
|
||||
describe("RoomUploadViewModel", () => {
|
||||
@@ -31,7 +37,7 @@ describe("RoomUploadViewModel", () => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it.each([true, false])("handles state of mayUpload when room.maySendMessage = %s", (maySendMessage) => {
|
||||
it.each([true, false])("handles state when room.maySendMessage = %s", (maySendMessage) => {
|
||||
room.maySendMessage.mockReturnValue(maySendMessage);
|
||||
const vm = new RoomUploadViewModel(
|
||||
room,
|
||||
@@ -42,10 +48,45 @@ describe("RoomUploadViewModel", () => {
|
||||
undefined,
|
||||
() => {},
|
||||
);
|
||||
expect(vm.getSnapshot().mayUpload).toEqual(maySendMessage);
|
||||
expect(vm.getSnapshot().options).toHaveLength(maySendMessage ? 1 : 0);
|
||||
room.maySendMessage.mockReturnValue(!maySendMessage);
|
||||
room.emit(RoomEvent.CurrentStateUpdated, room, null as any, null as any);
|
||||
expect(vm.getSnapshot().mayUpload).toEqual(!maySendMessage);
|
||||
expect(vm.getSnapshot().options).toHaveLength(maySendMessage ? 0 : 1);
|
||||
});
|
||||
|
||||
it("handles custom upload option", async () => {
|
||||
const compApi = new ComposerApi(new MatrixDispatcher());
|
||||
const replyEv = mkEvent({ type: "fake", content: {}, user: "any", event: true });
|
||||
const vm = new RoomUploadViewModel(
|
||||
room,
|
||||
client,
|
||||
TimelineRenderingType.Room,
|
||||
dis,
|
||||
replyEv,
|
||||
{
|
||||
rel_type: "any_type",
|
||||
},
|
||||
() => {},
|
||||
compApi,
|
||||
);
|
||||
const onSelected = jest.fn();
|
||||
const icon = { myicon: 5 } as any;
|
||||
compApi.addFileUploadOption({
|
||||
type: "org.example.test",
|
||||
label: "My uploader",
|
||||
icon,
|
||||
onSelected,
|
||||
});
|
||||
expect(vm.getSnapshot().options).toContainEqual({ type: "org.example.test", label: "My uploader", icon });
|
||||
vm.onUploadOptionSelected("org.example.test");
|
||||
expect(onSelected).toHaveBeenCalledWith(
|
||||
room.roomId,
|
||||
{ view: "room" },
|
||||
{
|
||||
inReplyToEventId: replyEv.getId(),
|
||||
relType: "any_type",
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("uploads via input", () => {
|
||||
@@ -171,4 +212,42 @@ describe("RoomUploadViewModel", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("RoomUploadContextProvider", () => {
|
||||
it("uploads when called via module API", async () => {
|
||||
sendContentListToRoomSpy.mockResolvedValue(undefined);
|
||||
render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<ScopedRoomContextProvider {...getRoomContext(room, {})}>
|
||||
<RoomUploadContextProvider>
|
||||
<p>Any child</p>
|
||||
</RoomUploadContextProvider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
const files = [
|
||||
{
|
||||
name: "fake.png",
|
||||
size: 1024,
|
||||
type: "image/png",
|
||||
},
|
||||
] as File[];
|
||||
defaultDispatcher.dispatch(
|
||||
{
|
||||
action: Action.ComposerFileInsert,
|
||||
files,
|
||||
timelineRenderingType: TimelineRenderingType.Room,
|
||||
} satisfies ComposerInsertFilesPayload,
|
||||
true,
|
||||
);
|
||||
expect(sendContentListToRoomSpy).toHaveBeenCalledWith(
|
||||
files,
|
||||
room.roomId,
|
||||
undefined,
|
||||
undefined,
|
||||
client,
|
||||
TimelineRenderingType.Room,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user