Fix share button in discovery settings being disabled incorrectly (#29151)
* Fix share button in discovery settings being disabled incorrectly Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve types & add tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add missing snapshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
committed by
GitHub
parent
aa01b17f9e
commit
4f1eac67a8
@@ -97,7 +97,7 @@ describe("ScalarAuthClient", function () {
|
||||
body: { errcode: "M_TERMS_NOT_SIGNED" },
|
||||
});
|
||||
sac.exchangeForScalarToken = jest.fn(() => Promise.resolve("testtoken1"));
|
||||
mocked(client.getTerms).mockResolvedValue({ policies: [] });
|
||||
mocked(client.getTerms).mockResolvedValue({ policies: {} });
|
||||
|
||||
await expect(sac.registerForToken()).resolves.toBe("testtoken1");
|
||||
});
|
||||
|
||||
@@ -6,9 +6,10 @@ 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 { MatrixEvent, EventType, SERVICE_TYPES } from "matrix-js-sdk/src/matrix";
|
||||
import { EventType, MatrixEvent, Policy, SERVICE_TYPES, Terms } from "matrix-js-sdk/src/matrix";
|
||||
import { screen, within } from "jest-matrix-react";
|
||||
|
||||
import { startTermsFlow, Service } from "../../src/Terms";
|
||||
import { dialogTermsInteractionCallback, Service, startTermsFlow } from "../../src/Terms";
|
||||
import { getMockClientWithEventEmitter } from "../test-utils";
|
||||
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
|
||||
|
||||
@@ -18,7 +19,7 @@ const POLICY_ONE = {
|
||||
name: "The first policy",
|
||||
url: "http://example.com/one",
|
||||
},
|
||||
};
|
||||
} satisfies Policy;
|
||||
|
||||
const POLICY_TWO = {
|
||||
version: "IX",
|
||||
@@ -26,7 +27,7 @@ const POLICY_TWO = {
|
||||
name: "The second policy",
|
||||
url: "http://example.com/two",
|
||||
},
|
||||
};
|
||||
} satisfies Policy;
|
||||
|
||||
const IM_SERVICE_ONE = new Service(SERVICE_TYPES.IM, "https://imone.test", "a token token");
|
||||
const IM_SERVICE_TWO = new Service(SERVICE_TYPES.IM, "https://imtwo.test", "a token token");
|
||||
@@ -42,7 +43,7 @@ describe("Terms", function () {
|
||||
beforeEach(function () {
|
||||
jest.clearAllMocks();
|
||||
mockClient.getAccountData.mockReturnValue(undefined);
|
||||
mockClient.getTerms.mockResolvedValue(null);
|
||||
mockClient.getTerms.mockResolvedValue({ policies: {} });
|
||||
mockClient.setAccountData.mockResolvedValue({});
|
||||
});
|
||||
|
||||
@@ -141,22 +142,25 @@ describe("Terms", function () {
|
||||
});
|
||||
mockClient.getAccountData.mockReturnValue(directEvent);
|
||||
|
||||
mockClient.getTerms.mockImplementation(async (_serviceTypes: SERVICE_TYPES, baseUrl: string) => {
|
||||
switch (baseUrl) {
|
||||
case "https://imone.test":
|
||||
return {
|
||||
policies: {
|
||||
policy_the_first: POLICY_ONE,
|
||||
},
|
||||
};
|
||||
case "https://imtwo.test":
|
||||
return {
|
||||
policies: {
|
||||
policy_the_second: POLICY_TWO,
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
mockClient.getTerms.mockImplementation(
|
||||
async (_serviceTypes: SERVICE_TYPES, baseUrl: string): Promise<Terms> => {
|
||||
switch (baseUrl) {
|
||||
case "https://imone.test":
|
||||
return {
|
||||
policies: {
|
||||
policy_the_first: POLICY_ONE,
|
||||
},
|
||||
};
|
||||
case "https://imtwo.test":
|
||||
return {
|
||||
policies: {
|
||||
policy_the_second: POLICY_TWO,
|
||||
},
|
||||
};
|
||||
}
|
||||
return { policies: {} };
|
||||
},
|
||||
);
|
||||
|
||||
const interactionCallback = jest.fn().mockResolvedValue(["http://example.com/one", "http://example.com/two"]);
|
||||
await startTermsFlow(mockClient, [IM_SERVICE_ONE, IM_SERVICE_TWO], interactionCallback);
|
||||
@@ -180,3 +184,29 @@ describe("Terms", function () {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("dialogTermsInteractionCallback", () => {
|
||||
it("should render a dialog with the expected terms", async () => {
|
||||
dialogTermsInteractionCallback(
|
||||
[
|
||||
{
|
||||
service: new Service(SERVICE_TYPES.IS, "http://base_url", "access_token"),
|
||||
policies: {
|
||||
sample: {
|
||||
version: "VERSION",
|
||||
en: {
|
||||
name: "Terms",
|
||||
url: "http://base_url/terms",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
expect(within(dialog).getByRole("link")).toHaveAttribute("href", "http://base_url/terms");
|
||||
expect(dialog).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
113
test/unit-tests/__snapshots__/Terms-test.tsx.snap
Normal file
113
test/unit-tests/__snapshots__/Terms-test.tsx.snap
Normal file
@@ -0,0 +1,113 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`dialogTermsInteractionCallback should render a dialog with the expected terms 1`] = `
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class=""
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Terms of Service
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
id="mx_Dialog_content"
|
||||
>
|
||||
<p>
|
||||
To continue you need to accept the terms of this service.
|
||||
</p>
|
||||
<table
|
||||
class="mx_TermsDialog_termsTable"
|
||||
>
|
||||
<tbody>
|
||||
<tr
|
||||
class="mx_TermsDialog_termsTableHeader"
|
||||
>
|
||||
<th>
|
||||
Service
|
||||
</th>
|
||||
<th>
|
||||
Summary
|
||||
</th>
|
||||
<th>
|
||||
Document
|
||||
</th>
|
||||
<th>
|
||||
Accept
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="mx_TermsDialog_service"
|
||||
>
|
||||
<div>
|
||||
Identity server
|
||||
<br />
|
||||
(
|
||||
base_url
|
||||
)
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="mx_TermsDialog_summary"
|
||||
>
|
||||
<div>
|
||||
Find others by phone or email
|
||||
<br />
|
||||
Be found by phone or email
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a
|
||||
class="mx_ExternalLink"
|
||||
href="http://base_url/terms"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
Terms
|
||||
<i
|
||||
class="mx_ExternalLink_icon"
|
||||
/>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="checkbox"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -10,10 +10,12 @@ import React from "react";
|
||||
import { render, screen, waitFor, act, fireEvent } from "jest-matrix-react";
|
||||
import { AuthType } from "matrix-js-sdk/src/interactive-auth";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { Policy } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import {
|
||||
EmailIdentityAuthEntry,
|
||||
MasUnlockCrossSigningAuthEntry,
|
||||
TermsAuthEntry,
|
||||
} from "../../../../../src/components/views/auth/InteractiveAuthEntryComponents";
|
||||
import { createTestClient } from "../../../../test-utils";
|
||||
|
||||
@@ -99,3 +101,38 @@ describe("<MasUnlockCrossSigningAuthEntry/>", () => {
|
||||
expect(submitAuthDict).toHaveBeenCalledWith({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("<TermsAuthEntry/>", () => {
|
||||
const renderAuth = (policy: Policy, props = {}) => {
|
||||
const matrixClient = createTestClient();
|
||||
|
||||
return render(
|
||||
<TermsAuthEntry
|
||||
matrixClient={matrixClient}
|
||||
loginType={AuthType.Email}
|
||||
onPhaseChange={jest.fn()}
|
||||
submitAuthDict={jest.fn()}
|
||||
fail={jest.fn()}
|
||||
clientSecret="my secret"
|
||||
showContinue={true}
|
||||
stageParams={{
|
||||
policies: {
|
||||
test_policy: policy,
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
/>,
|
||||
);
|
||||
};
|
||||
|
||||
test("should render", () => {
|
||||
const { container } = renderAuth({
|
||||
version: "alpha",
|
||||
en: {
|
||||
name: "Test Policy",
|
||||
url: "https://example.com/en",
|
||||
},
|
||||
});
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,3 +82,38 @@ exports[`<MasUnlockCrossSigningAuthEntry/> should render 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<TermsAuthEntry/> should render 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_InteractiveAuthEntryComponents"
|
||||
>
|
||||
<p>
|
||||
Please review and accept the policies of this homeserver:
|
||||
</p>
|
||||
<label
|
||||
class="mx_InteractiveAuthEntryComponents_termsPolicy"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
/>
|
||||
<a
|
||||
href="https://example.com/en"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
Test Policy
|
||||
</a>
|
||||
</label>
|
||||
<div
|
||||
aria-disabled="true"
|
||||
class="mx_AccessibleButton mx_InteractiveAuthEntryComponents_termsSubmit mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary mx_AccessibleButton_disabled"
|
||||
disabled=""
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Accept
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React from "react";
|
||||
import { act, render, screen } from "jest-matrix-react";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixClient, MatrixEvent, Terms, ThreepidMedium } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
@@ -26,6 +26,18 @@ jest.mock("../../../../../../src/IdentityAuthClient", () =>
|
||||
})),
|
||||
);
|
||||
|
||||
const sampleTerms = {
|
||||
policies: {
|
||||
terms: { version: "alpha", en: { name: "No ball games", url: "https://foobar" } },
|
||||
},
|
||||
} satisfies Terms;
|
||||
|
||||
const invalidTerms = {
|
||||
policies: {
|
||||
terms: { version: "invalid" },
|
||||
},
|
||||
} satisfies Terms;
|
||||
|
||||
describe("DiscoverySettings", () => {
|
||||
let client: MatrixClient;
|
||||
|
||||
@@ -51,20 +63,17 @@ describe("DiscoverySettings", () => {
|
||||
|
||||
it("displays alert if an identity server needs terms accepting", async () => {
|
||||
mocked(client).getIdentityServerUrl.mockReturnValue("https://example.com");
|
||||
mocked(client).getTerms.mockResolvedValue({
|
||||
["policies"]: { en: "No ball games" },
|
||||
});
|
||||
mocked(client).getTerms.mockResolvedValue(sampleTerms);
|
||||
|
||||
render(<DiscoverySettings />, { wrapper: DiscoveryWrapper });
|
||||
|
||||
await expect(await screen.findByText("Let people find you")).toBeInTheDocument();
|
||||
expect(await screen.findByText("Let people find you")).toBeInTheDocument();
|
||||
expect(screen.getByRole("link")).toHaveAttribute("href", "https://foobar");
|
||||
});
|
||||
|
||||
it("button to accept terms is disabled if checkbox not checked", async () => {
|
||||
mocked(client).getIdentityServerUrl.mockReturnValue("https://example.com");
|
||||
mocked(client).getTerms.mockResolvedValue({
|
||||
["policies"]: { en: "No ball games" },
|
||||
});
|
||||
mocked(client).getTerms.mockResolvedValue(sampleTerms);
|
||||
|
||||
render(<DiscoverySettings />, { wrapper: DiscoveryWrapper });
|
||||
|
||||
@@ -93,4 +102,40 @@ describe("DiscoverySettings", () => {
|
||||
|
||||
expect(client.getThreePids).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not disable share button if terms accepted", async () => {
|
||||
mocked(client).getThreePids.mockResolvedValue({
|
||||
threepids: [
|
||||
{
|
||||
medium: ThreepidMedium.Email,
|
||||
address: "test@email.com",
|
||||
bound: false,
|
||||
added_at: 123,
|
||||
validated_at: 234,
|
||||
},
|
||||
],
|
||||
});
|
||||
mocked(client).getIdentityServerUrl.mockReturnValue("https://example.com");
|
||||
mocked(client).getTerms.mockResolvedValue(sampleTerms);
|
||||
mocked(client).getAccountData.mockReturnValue(
|
||||
new MatrixEvent({
|
||||
content: { accepted: [sampleTerms.policies["terms"]["en"].url] },
|
||||
}),
|
||||
);
|
||||
|
||||
render(<DiscoverySettings />, { wrapper: DiscoveryWrapper });
|
||||
|
||||
const shareButton = await screen.findByRole("button", { name: "Share" });
|
||||
expect(shareButton).not.toHaveAttribute("aria-disabled", "true");
|
||||
});
|
||||
|
||||
it("should not show invalid terms", async () => {
|
||||
mocked(client).getIdentityServerUrl.mockReturnValue("https://example.com");
|
||||
mocked(client).getTerms.mockResolvedValue(invalidTerms);
|
||||
|
||||
render(<DiscoverySettings />, { wrapper: DiscoveryWrapper });
|
||||
|
||||
expect(await screen.findByText("Let people find you")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("link")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user