stitch-ui
Version:
373 lines (333 loc) • 11.5 kB
JavaScript
/* global it, describe, beforeAll, afterEach, expect, test */
import React from "react"; // eslint-disable-line no-unused-vars
import { Provider } from "react-redux";
import { JSDOM } from "jsdom";
import { MemoryRouter } from "react-router-dom";
import { mount, ReactWrapper } from "enzyme";
import {
testSetup,
noConsoleErrorsAllowed,
stubConfirmation
} from "../../testutil";
import * as homeActions from "../../home/actions";
import * as actions from "../actions";
import Authentication from "../components/Authentication";
import AllowedRequestOrigins from "../components/AllowedRequestOrigins";
global.navigator = {
userAgent: "node.js"
};
const doc = new JSDOM("<!doctype html><html><body></body></html>");
global.document = doc;
global.window = doc.defaultView;
describe("auth", () => {
noConsoleErrorsAllowed();
let store;
let testApp;
let display;
let actionSub;
beforeAll(async () => {
const testHarness = await testSetup();
store = testHarness.store;
actionSub = testHarness.actionSub;
testApp = await store.dispatch(
homeActions.createApp(testHarness.groupId, "my-test-app")
);
return new Promise(resolve => {
actionSub.subscribe(actions.loadProvidersActions.rcv.getType(), resolve);
display = mount(
<Provider store={store}>
<MemoryRouter>
<div>
<Authentication app={testApp} match={{ url: "/" }} />
</div>
</MemoryRouter>
</Provider>
);
});
});
afterEach(() => {
actionSub.reset();
});
it("loads the providers", async () => {
expect(display.find("AuthProvider").length).toBe(6);
});
it("can enable and disable anonymous login", async () => {
// Open the modal
display
.find("AuthProvider")
.findWhere(x => x.props().title === "Allow users to log in anonymously")
.find("button")
.findWhere(x => x.text() === "Edit")
.props()
.onClick();
const anonLoginModal = display
.find("AuthProvider")
.findWhere(x => x.props().title === "Allow users to log in anonymously")
.at(0).node.modal.portal;
const anonModalContent = new ReactWrapper(anonLoginModal, anonLoginModal);
expect(store.getState().auth.auth.providers).toEqual({});
await anonModalContent
.find("input[type='checkbox']")
.props()
.onChange({ target: { checked: false } });
expect(store.getState().auth.auth.providers).toEqual({});
});
it("get an error when saving an oauth provider with invalid data", async () => {
const goog = display
.find("AuthProvider")
.findWhere(x => x.props().title === "Google");
goog.find("button").findWhere(x => x.text() === "Edit").props().onClick();
const googModal = new ReactWrapper(
goog.node.modal.portal,
goog.node.modal.portal
);
googModal
.find("input[id='enable-Google']")
.props()
.onChange({ target: { checked: true } });
await googModal
.find("input[name='clientId']")
.props()
.onChange({ target: { value: "testclientid" } });
await googModal
.find("button")
.findWhere(b => b.text() === "Save")
.props()
.onClick();
expect(googModal.find(".banner-error")).toHaveLength(1);
expect(googModal.find(".banner-error").text()).toContain(
"clientSecret is a required string for OAuth2 configuration"
);
});
it("can save an oauth provider successfully", async () => {
const goog = display
.find("AuthProvider")
.findWhere(x => x.props().title === "Google");
goog.find("button").findWhere(x => x.text() === "Edit").props().onClick();
const googModal = new ReactWrapper(
goog.node.modal.portal,
goog.node.modal.portal
);
googModal
.find("input[id='enable-Google']")
.props()
.onChange({ target: { checked: true } });
googModal
.find("input[name='clientId']")
.props()
.onChange({ target: { value: "testclientid" } });
googModal
.find("input[name='clientSecret']")
.props()
.onChange({ target: { value: "testclientsecret" } });
googModal.find("button[name='addRedirectURI']").simulate("click");
googModal.find("button[name='addDomainRestriction']").simulate("click");
googModal
.find("input[name='redirectURI/0']")
.props()
.onChange({ target: { value: "http://localhost:8000" } });
googModal
.find("input[name='domainRestriction/0']")
.props()
.onChange({ target: { value: "domain.com" } });
// ensure empty redirect URIs and domain restrictions are ignored
googModal.find("button[name='addRedirectURI']").simulate("click");
googModal.find("button[name='addDomainRestriction']").simulate("click");
googModal
.find("input[name='metadataFields/email']")
.props()
.onChange({ target: { checked: true } });
await googModal
.find("button")
.findWhere(b => b.text() === "Save")
.props()
.onClick();
expect(googModal.find(".banner-error")).toHaveLength(0);
expect(store.getState().auth.auth.providers).toEqual({
"oauth2/google": {
clientId: "testclientid",
clientSecret: "testclientsecret",
redirectURIs: ["http://localhost:8000"],
domainRestrictions: ["domain.com"],
metadataFields: ["email"]
}
});
});
it("can create/enable/disable/delete an API key", async () => {
const apiKeys = display
.find("AuthProvider")
.findWhere(x => x.props().title === "API Keys");
apiKeys
.find("button")
.findWhere(x => x.text() === "Edit")
.props()
.onClick();
const apiKeyModal = new ReactWrapper(
apiKeys.node.modal.portal,
apiKeys.node.modal.portal
);
expect(apiKeyModal.find("input[type='checkbox']")).toHaveLength(1);
expect(store.getState().auth.auth.providers).not.toHaveProperty("api/key");
// enable api/key provider
await apiKeyModal
.find("input[type='checkbox']")
.props()
.onChange({ target: { checked: true } });
expect(store.getState().auth.auth.providers).toHaveProperty("api/key");
// disable api/key provider
await apiKeyModal
.find("input[type='checkbox']")
.props()
.onChange({ target: { checked: false } });
expect(store.getState().auth.auth.providers).not.toHaveProperty("api/key");
apiKeyModal.find("button[name='createAPIKey']").simulate("click");
apiKeyModal
.find("input[name='newKeyName']")
.props()
.onChange({ target: { value: "newname" } });
// Create new key
await apiKeyModal
.find("button")
.findWhere(x => x.text() === "Save")
.props()
.onClick();
const apiKeysStored = store.getState().auth.apiKeys.apiKeys.toJS();
const apiKeyKey = Object.keys(apiKeysStored)[0];
expect(Object.keys(apiKeysStored)).toHaveLength(1);
expect(apiKeysStored[apiKeyKey].name).toEqual("newname");
expect(apiKeyModal.find("input[type='checkbox']")).toHaveLength(2);
// Disable it
await apiKeyModal
.find("input[type='checkbox']")
.at(1)
.props()
.onChange({ target: { checked: false } });
expect(store.getState().auth.apiKeys.apiKeys.get(apiKeyKey).disabled).toBe(
true
);
// Delete it
stubConfirmation(true);
await apiKeyModal.find("button[name='deleteAPIKey']").props().onClick();
expect(store.getState().auth.apiKeys.apiKeys.toJS()).toEqual({});
// Attempt to add new key with invalid name
apiKeyModal.find("button[name='createAPIKey']").simulate("click");
apiKeyModal
.find("input[name='newKeyName']")
.props()
.onChange({ target: { value: "invalid newname" } });
await apiKeyModal
.find("button")
.findWhere(x => x.text() === "Save")
.props()
.onClick();
expect(store.getState().auth.apiKeys.apiKeys.toJS()).toEqual({});
expect(apiKeyModal.find(".banner-error")).toHaveLength(1);
expect(apiKeyModal.find(".banner-error").text()).toMatch(
"can only contain ASCII letters, numbers, underscores, and hyphens"
);
// Attempt to add new key with empty name
apiKeyModal.find("button[name='createAPIKey']").simulate("click");
apiKeyModal
.find("input[name='newKeyName']")
.props()
.onChange({ target: { value: "" } });
await apiKeyModal
.find("button")
.findWhere(x => x.text() === "Save")
.props()
.onClick();
expect(store.getState().auth.apiKeys.apiKeys.toJS()).toEqual({});
expect(apiKeyModal.find(".banner-error")).toHaveLength(1);
expect(apiKeyModal.find(".banner-error").text()).toMatch(
"'name' is a required string"
);
});
});
describe("allowed request origins", () => {
noConsoleErrorsAllowed();
let store;
let testApp;
let display;
let actionSub;
beforeAll(async () => {
const testHarness = await testSetup();
store = testHarness.store;
actionSub = testHarness.actionSub;
testApp = await store.dispatch(
homeActions.createApp(testHarness.groupId, "my-test-app")
);
display = mount(
<Provider store={store}>
<MemoryRouter>
<div>
<AllowedRequestOrigins app={testApp} match={{ url: "/" }} />
</div>
</MemoryRouter>
</Provider>
);
});
afterEach(() => {
actionSub.reset();
});
let modalContent;
it("can show the modal", async () => {
// Open the modal
display
.find("AllowedRequestOrigins")
.find("button")
.findWhere(x => x.text() === "Edit")
.props()
.onClick();
const originsModal = display
.find("AllowedRequestOrigins")
.find("Modal")
.at(0).node.portal;
modalContent = new ReactWrapper(originsModal, originsModal);
expect(
modalContent.find("button[name='addAllowedRequestOrigin']").length
).toBe(1);
});
test("saving invalid origins fails", async () => {
const addButton = modalContent.find(
"button[name='addAllowedRequestOrigin']"
);
await addButton.props().onClick();
await addButton.props().onClick();
await addButton.props().onClick();
const saveButton = modalContent.find(
"button[name='saveAllowedRequestOrigins']"
);
await saveButton.props().onClick();
expect(modalContent.find(".banner-error").text()).toContain(
"invalid origin"
);
});
test("correcting only some of the origins and saving again should still fail", async () => {
await modalContent
.find("input[name='origin/0']")
.props()
.onChange({ target: { value: "https://origin1.com" } });
const saveButton = modalContent.find(
"button[name='saveAllowedRequestOrigins']"
);
await saveButton.props().onClick();
expect(modalContent.find(".banner-error").text()).toContain(
"invalid origin"
);
});
test("correcting all the origins and saving again works", async () => {
await modalContent
.find("input[name='origin/1']")
.props()
.onChange({ target: { value: "https://origin2.com" } });
await modalContent
.find("input[name='origin/2']")
.props()
.onChange({ target: { value: "https://origin3.com" } });
const saveButton = modalContent.find(
"button[name='saveAllowedRequestOrigins']"
);
await saveButton.props().onClick();
expect(modalContent.find("Error").at(1).find("div").length).toEqual(0);
});
});