Skip to content

Commit

Permalink
test(sso): add test for email precheck, registration and welcome comp…
Browse files Browse the repository at this point in the history
…onent
  • Loading branch information
marc.sirisak committed Oct 2, 2024
1 parent b574d43 commit eab8945
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 10 deletions.
3 changes: 1 addition & 2 deletions config.preprod.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@
"feature_space": ["*"],
"feature_audio_call": ["i.tchap.gouv.fr", "e.tchap.gouv.fr"],
"feature_video_call": ["i.tchap.gouv.fr", "e.tchap.gouv.fr"],
"feature_screenshare_call": ["*"],
"feature_sso_flow": []
"feature_screenshare_call": ["*"]
},
"tchap_sso_flow": {
"isActive": false
Expand Down
3 changes: 1 addition & 2 deletions config.prod.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,7 @@
"feature_space": ["*"],
"feature_audio_call": ["*"],
"feature_video_call": ["agent.dinum.tchap.gouv.fr"],
"feature_screenshare_call": ["*"],
"feature_sso_flow": []
"feature_screenshare_call": ["*"]
},
"tchap_sso_flow": {
"isActive": false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default class Welcome extends React.PureComponent<IProps> {
replaceMap["$logoUrl"] = logoUrl;
// :TCHAP: sso-agentconnect-flow - pageUrl = "welcome.html";
pageUrl = TchapUIFeature.isSSOFlowActive() ? "welcome_sso.html" : "welcome.html";
// end :TCHAP:
}

return (
Expand Down
5 changes: 4 additions & 1 deletion patches/subtree-modifications.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@
"sso-agentconnect-flow": {
"issue": "https://github.com/tchapgouv/tchap-web-v4/issues/386",
"files": [
"src/components/structures/MatrixChat.tsx"
"src/components/structures/MatrixChat.tsx",
"src/components/structures/auth/Registration.tsx",
"src/components/structures/auth/Login.tsx",
"src/components/views/auth/Welcome.tsx"
]
}
}
7 changes: 2 additions & 5 deletions src/tchap/components/views/sso/EmailVerificationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,7 @@ export default function EmailVerificationPage() {
const isFieldCorrect = await emailFieldRef.current?.validate({ allowEmpty: false });

if (!isFieldCorrect) {
emailFieldRef.current?.focus();
emailFieldRef.current?.validate({ allowEmpty: false, focused: true });
setErrorText(_td("auth|sso|error_email"));
setLoading(false);
displayError(_td("auth|sso|error_email"));
return;
}

Expand Down Expand Up @@ -135,7 +132,7 @@ export default function EmailVerificationPage() {
/>
</div>
{errorText && <ErrorMessage message={errorText} />}
<button type="submit" className="tc_ButtonParent tc_ButtonProconnect tc_Button_iconPC">
<button type="submit" data-testid="proconnect-submit" className="tc_ButtonParent tc_ButtonProconnect tc_Button_iconPC">
{submitButtonChild}
</button>
<div className="mx_AuthBody_button-container tc_bottomButton">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import React from "react";
import { render, cleanup, fireEvent, screen, act } from "@testing-library/react";
import { mocked, MockedObject } from "jest-mock";
import { MatrixClient } from "matrix-js-sdk/src/matrix";

import EmailVerificationPage from "~tchap-web/src/tchap/components/views/sso/EmailVerificationPage";
import TchapUtils from "~tchap-web/src/tchap/util/TchapUtils";
import { ValidatedServerConfig } from "~matrix-react-sdk/src/utils/ValidatedServerConfig";
import { mockPlatformPeg, stubClient } from "~matrix-react-sdk/test/test-utils";
import BasePlatform from "~matrix-react-sdk/src/BasePlatform";
import Login from "~matrix-react-sdk/src/Login";

jest.mock("~matrix-react-sdk/src/PlatformPeg")
jest.mock("~tchap-web/src/tchap/util/TchapUtils");
jest.mock("~matrix-react-sdk/src/Login");

describe("<EmailVerificationPage />", () => {

const userEmail = "marc@tchap.beta.gouv.fr";
const defaultHsUrl = "https://matrix.agent1.fr"
const secondHsUrl = "https://matrix.agent2.fr"


const PlatformPegMocked: MockedObject<BasePlatform> = mockPlatformPeg();
const mockedClient: MatrixClient = stubClient();
const mockedTchapUtils = mocked(TchapUtils);

const mockLoginObject = (hs: string = defaultHsUrl) => {
const mockLoginObject = mocked(new Login(hs, hs, null, {}));
mockLoginObject.createTemporaryClient.mockImplementation(() => mockedClient);
return mockLoginObject;
}

const mockedFetchHomeserverFromEmail = (hs: string = defaultHsUrl) => {
mockedTchapUtils.fetchHomeserverForEmail.mockImplementation(() => (Promise.resolve({ base_url: hs, server_name: hs})));
}

const mockedValidatedServerConfig = (withError: boolean = false, hsUrl: string = defaultHsUrl) => {
if (withError) {
mockedTchapUtils.makeValidatedServerConfig.mockImplementation(() => {throw new Error()});
} else {
mockedTchapUtils.makeValidatedServerConfig.mockImplementation(() => (Promise.resolve({
hsUrl: defaultHsUrl,
hsName: "hs",
hsNameIsDifferent: false,
isUrl: "",
isDefault: true,
isNameResolvable: true,
warning: ""
} as ValidatedServerConfig)))
}
}

const mockedPlatformPegStartSSO = (withError: boolean) => {
if (withError) {
jest.spyOn(PlatformPegMocked, "startSingleSignOn").mockImplementation(() => {throw new Error()})
} else {
jest.spyOn(PlatformPegMocked, "startSingleSignOn").mockImplementation(() => {})
}
}

const renderEmailVerificationPage = () => render(<EmailVerificationPage />);

beforeEach(() => {
mockLoginObject(defaultHsUrl);
})

afterEach(() => {
cleanup();
jest.restoreAllMocks();
});

it("returns error when empty email", async () => {
const { container } = renderEmailVerificationPage();

// Put text in email field
const emailField = screen.getByRole("textbox");
fireEvent.focus(emailField);
fireEvent.change(emailField, { target: { value: "" } });

// click on proconnect button
const proconnectButton = screen.getByTestId("proconnect-submit");
await act(async () => {
await fireEvent.click(proconnectButton);
})

// Error classes should not appear
expect(container.getElementsByClassName("mx_ErrorMessage").length).toBe(1);
});

it("returns inccorrect email", async () => {
const { container } = renderEmailVerificationPage();

// Put text in email field
const emailField = screen.getByRole("textbox");
fireEvent.focus(emailField);
fireEvent.change(emailField, { target: { value: "falseemail" } });

// click on proconnect button
const proconnectButton = screen.getByTestId("proconnect-submit");
await act(async () => {
await fireEvent.click(proconnectButton);
})

// Error classes should not appear
expect(container.getElementsByClassName("mx_ErrorMessage").length).toBe(1);
});


it("should throw error when homeserver catch an error", async () => {
const { container } = renderEmailVerificationPage();

// mock server returns an errorn, we dont need to mock the other implementation
// since the code should throw an error before accessing them
mockedValidatedServerConfig(true);

// Put text in email field
const emailField = screen.getByRole("textbox");
fireEvent.focus(emailField);
fireEvent.change(emailField, { target: { value: userEmail } });

// click on proconnect button
const proconnectButton = screen.getByTestId("proconnect-submit");
await act(async () => {
await fireEvent.click(proconnectButton);
})

// Error classes should not appear
expect(container.getElementsByClassName("mx_ErrorMessage").length).toBe(1);
})


it("should throw and error when connecting to proconnect error", async () => {
const { container } = renderEmailVerificationPage();

mockedValidatedServerConfig(false);
// mock platform page startsso error
mockedPlatformPegStartSSO(true);

// Put text in email field
const emailField = screen.getByRole("textbox");
fireEvent.focus(emailField);
fireEvent.change(emailField, { target: { value: userEmail } });

// click on proconnect button
const proconnectButton = screen.getByTestId("proconnect-submit");
await act(async () => {
await fireEvent.click(proconnectButton);
})

// Error classes should not appear
expect(container.getElementsByClassName("mx_ErrorMessage").length).toBe(1);
})


it("should start sso with correct homeserver 1", async () => {
renderEmailVerificationPage();

// Mock the implementation without error, what we want is to be sure they are called with the correct parameters
mockedFetchHomeserverFromEmail(defaultHsUrl);
mockedValidatedServerConfig(false, defaultHsUrl)
mockedPlatformPegStartSSO(false);

// Put text in email field
const emailField = screen.getByRole("textbox");
fireEvent.focus(emailField);
fireEvent.change(emailField, { target: { value: userEmail } });

// click on proconnect button
const proconnectButton = screen.getByTestId("proconnect-submit");
await act(async () => {
await fireEvent.click(proconnectButton);
})

expect(mockedTchapUtils.makeValidatedServerConfig).toHaveBeenCalledWith({ base_url: defaultHsUrl, server_name: defaultHsUrl});
expect(PlatformPegMocked.startSingleSignOn).toHaveBeenCalledTimes(1);
});

it("should start sso with correct homeserver 2", async () => {
renderEmailVerificationPage();

// Mock the implementation without error, what we want is to be sure they are called with the correct parameters
mockedFetchHomeserverFromEmail(secondHsUrl);
mockedValidatedServerConfig(false, secondHsUrl);
mockedPlatformPegStartSSO(false);

// Put text in email field
const emailField = screen.getByRole("textbox");
fireEvent.focus(emailField);
fireEvent.change(emailField, { target: { value: userEmail } });

// click on proconnect button
const proconnectButton = screen.getByTestId("proconnect-submit");
await act(async () => {
await fireEvent.click(proconnectButton);
})

expect(mockedTchapUtils.makeValidatedServerConfig).toHaveBeenCalledWith({ base_url: secondHsUrl, server_name: secondHsUrl});
expect(PlatformPegMocked.startSingleSignOn).toHaveBeenCalledTimes(1);
});
});
42 changes: 42 additions & 0 deletions test/unit-tests/tchap/components/views/sso/Register-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react";
import { render, cleanup } from "@testing-library/react";

import SdkConfig, { ConfigOptions } from "~matrix-react-sdk/src/SdkConfig";
import Register from "~matrix-react-sdk/src/components/structures/auth/Registration";
import { IMatrixClientCreds } from "~tchap-web/linked-dependencies/matrix-react-sdk/src/MatrixClientPeg";
import { ValidatedServerConfig } from "~tchap-web/linked-dependencies/matrix-react-sdk/src/utils/ValidatedServerConfig";


describe("<Register />", () => {

const addSSOFlowToMockConfig = (isActive: boolean = false) => {
// mock SdkConfig.get("tchap_features")
const config: ConfigOptions = { tchap_sso_flow: { isActive } };
SdkConfig.put(config);
};

const renderRegister = () => render(<Register
serverConfig={{} as ValidatedServerConfig}
onLoggedIn={(params: IMatrixClientCreds, password: string): Promise<void> =>{ return Promise.resolve()}}
onLoginClick={() => {}}
onServerConfigChange={(config: ValidatedServerConfig) => {}}
/>);

afterEach(() => {
cleanup();
});

it("returns proconnect button in register when the config include sso flow", () => {
addSSOFlowToMockConfig(true);
const { container } = renderRegister();

expect(container.getElementsByClassName("tc_pronnect").length).toBe(1);

Check failure on line 33 in test/unit-tests/tchap/components/views/sso/Register-test.tsx

View workflow job for this annotation

GitHub Actions / Jest

<Register /> › returns proconnect button in register when the config include sso flow

expect(received).toBe(expected) // Object.is equality Expected: 1 Received: 0 at Object.toBe (test/unit-tests/tchap/components/views/sso/Register-test.tsx:33:72)
});

it("returns no proconnect button when the config does'nt include sso flow", () => {
addSSOFlowToMockConfig(false);
const { container } = renderRegister();

expect(container.getElementsByClassName("tc_pronnect").length).toBe(0);
});
});
36 changes: 36 additions & 0 deletions test/unit-tests/tchap/components/views/sso/Welcome-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react";
import { render, cleanup } from "@testing-library/react";

import SdkConfig, { ConfigOptions } from "~matrix-react-sdk/src/SdkConfig";
import Welcome from "~matrix-react-sdk/src/components/views/auth/Welcome";


describe("<Welcome />", () => {

const addSSOFlowToMockConfig = (isActive: boolean = false) => {
// mock SdkConfig.get("tchap_features")
const config: ConfigOptions = { tchap_sso_flow: { isActive } };
SdkConfig.put(config);
};

const renderWelcomePage = () => render(<Welcome />);

afterEach(() => {
cleanup();
});

it("returns welcome_sso html when sso_flow is active in config ", () => {
addSSOFlowToMockConfig(true);
const { container } = renderWelcomePage();

expect(container.getElementsByClassName("tc_proconnect").length).toBe(1);

Check failure on line 26 in test/unit-tests/tchap/components/views/sso/Welcome-test.tsx

View workflow job for this annotation

GitHub Actions / Jest

<Welcome /> › returns welcome_sso html when sso_flow is active in config

expect(received).toBe(expected) // Object.is equality Expected: 1 Received: 0 at Object.toBe (test/unit-tests/tchap/components/views/sso/Welcome-test.tsx:26:74)
});

it("returns normal welcome html page without sso flow ", async () => {
addSSOFlowToMockConfig(false);
const { container } = renderWelcomePage();

expect(container.getElementsByClassName("tc_proconnect").length).toBe(0);
expect(container.getElementsByClassName("mx_Button_iconSignIn").length).toBe(1);

Check failure on line 34 in test/unit-tests/tchap/components/views/sso/Welcome-test.tsx

View workflow job for this annotation

GitHub Actions / Jest

<Welcome /> › returns normal welcome html page without sso flow

expect(received).toBe(expected) // Object.is equality Expected: 1 Received: 0 at Object.toBe (test/unit-tests/tchap/components/views/sso/Welcome-test.tsx:34:81)
});
});

0 comments on commit eab8945

Please sign in to comment.