Skip to content
Snippets Groups Projects
Unverified Commit ff5c6e4f authored by Tom Robinson's avatar Tom Robinson Committed by GitHub
Browse files

Switch Cypress snapshot generation to just use API calls directly (#11746)

* Switch Cypress snapshot generation to just use API calls directly
parent f7e4135b
Branches
Tags
No related merge requests found
import "@testing-library/cypress/add-commands";
export const ADMIN_CREDS = {
username: "bob@metabase.com",
password: "12341234",
export const USERS = {
admin: {
first_name: "Bobby",
last_name: "Tables",
username: "admin@metabase.com",
password: "12341234",
},
normal: {
first_name: "Robert",
last_name: "Tableton",
username: "normal@metabase.com",
password: "12341234",
},
nodata: {
first_name: "No Data",
last_name: "Tableton",
username: "nodata@metabase.com",
password: "12341234",
},
nocollection: {
first_name: "No Collection",
last_name: "Tableton",
username: "nocollection@metabase.com",
password: "12341234",
},
none: {
first_name: "None",
last_name: "Tableton",
username: "none@metabase.com",
password: "12341234",
},
};
export const NORMAL_USER_CREDS = {
username: "robert@metabase.com",
password: "12341234",
};
export function signIn(user = "admin") {
cy.request("POST", "/api/session", USERS[user]);
}
export function signOut() {
cy.clearCookie("metabase.SESSION");
}
export function signInAsAdmin() {
cy.request("POST", "/api/session", ADMIN_CREDS);
signIn("admin");
}
export function signInAsNormalUser() {
cy.request("POST", "/api/session", NORMAL_USER_CREDS);
}
export function signOut() {
cy.clearCookie("metabase.SESSION");
signIn("normal");
}
export function snapshot(name) {
......
{
"testFiles": "**/*.cy.spec.js",
"pluginsFile": "frontend/test/cypress-plugins.js",
"integrationFolder": "frontend/test",
"supportFile": "frontend/test/__support__/cypress.js"
"testFiles": "**/*.cy.spec.js",
"pluginsFile": "frontend/test/cypress-plugins.js",
"integrationFolder": "frontend/test",
"supportFile": "frontend/test/__support__/cypress.js",
"viewportHeight": 800,
"viewportWidth": 1280
}
import { restore } from "__support__/cypress";
import { restore, signOut, USERS } from "__support__/cypress";
describe("sign in", () => {
before(restore);
beforeEach(signOut);
it("should display an error for incorrect passwords", () => {
it("should redirect to /auth/login", () => {
cy.visit("/");
// confirm we're redirected to /auth/login when not logged in
cy.url().should("contain", "auth/login");
});
it("should display an error for incorrect passwords", () => {
cy.visit("/");
cy.findByLabelText("Email address").type(USERS.admin.username);
cy.findByLabelText("Password").type("INVALID" + USERS.admin.password);
cy.findByText("Sign in").click();
cy.contains("did not match stored password");
});
cy.findByLabelText("Email address").type("bobby@metabase.com");
cy.findByLabelText("Password").type("password"); // invalid password
it("should display same error for unknown users (to avoid leaking the existence of accounts)", () => {
cy.visit("/");
cy.findByLabelText("Email address").type("INVALID" + USERS.admin.username);
cy.findByLabelText("Password").type(USERS.admin.password);
cy.findByText("Sign in").click();
cy.contains("did not match stored password");
});
it("should greet users after successful login", () => {
cy.visit("/auth/login");
cy.findByLabelText("Email address").type("bob@metabase.com");
cy.findByLabelText("Password").type("12341234"); // valid
cy.findByLabelText("Email address").type(USERS.admin.username);
cy.findByLabelText("Password").type(USERS.admin.password);
cy.findByText("Sign in").click();
cy.contains(/[a-z ]+, Bob/i);
});
......
import {
signInAsAdmin,
signInAsNormalUser,
signIn,
signOut,
restore,
popover,
......@@ -13,9 +13,11 @@ const COUNT_DOOHICKEY = "42";
const PUBLIC_URL_REGEX = /\/public\/(question|dashboard)\/[0-9a-f-]+$/;
const ADMIN_USER = ["admin", signInAsAdmin];
const NORMAL_USER = ["normal", signInAsNormalUser];
const ANONYMOUS_USER = ["anonymous", signOut];
const USERS = {
"admin user": () => signInAsAdmin(),
"user with no permissions": () => signIn("none"),
"anonymous user": () => signOut(),
};
describe("public and embeds", () => {
before(restore);
......@@ -38,9 +40,17 @@ describe("public and embeds", () => {
cy.visit(`/question/new?type=native&database=${SAMPLE_DATASET.id}`);
cy.get(".ace_text-input").type(
"select count(*) from products where {{c}}",
{ force: true, parseSpecialCharSequences: false },
{
force: true,
parseSpecialCharSequences: false,
},
);
// HACK: disable the editor due to weirdness with `.type()` on other elements typing into editor
cy.window().then(win => {
win.document.querySelectorAll(".ace_text-input")[0].disabled = true;
});
cy.contains("Filter label")
.siblings("input")
.type("Category");
......@@ -84,6 +94,7 @@ describe("public and embeds", () => {
cy.contains("Save").click();
modal()
.find('input[name="name"]')
.focus()
.type("sql param");
modal()
.contains("button", "Save")
......@@ -233,8 +244,8 @@ describe("public and embeds", () => {
});
});
[ADMIN_USER, NORMAL_USER, ANONYMOUS_USER].map(([userType, setUser]) =>
describe(`${userType} user`, () => {
Object.entries(USERS).map(([userType, setUser]) =>
describe(`${userType}`, () => {
beforeEach(setUser);
it(`should be able to view public questions`, () => {
......
......@@ -144,7 +144,7 @@ describe("query builder", () => {
.contains("button", "Save")
.click();
cy.contains("Yes please!").click();
cy.contains("orders in a dashboard").click();
cy.contains("Orders in a dashboard").click();
// create a new question to see if the "add to a dashboard" modal is still there
cy.contains("Browse Data").click();
......
import {
snapshot,
restore,
ADMIN_CREDS,
NORMAL_USER_CREDS,
modal,
} from "__support__/cypress";
import { snapshot, restore, USERS } from "__support__/cypress";
describe("default", () => {
it("default", () => {
snapshot("blank");
setup();
updateSettings();
addUsersAndGroups();
createQuestionAndDashboard();
addUser();
snapshot("default");
restore("blank");
});
});
function setup() {
cy.visit("/");
cy.contains("Let's get started").click();
// User
const { username, password } = ADMIN_CREDS;
cy.get('input[name="first_name"]').type("Bobby");
cy.get('input[name="last_name"]').type("Tables");
cy.get('input[name="email"]').type(username);
cy.get('input[name="password"]').type(password);
cy.get('input[name="password_confirm"]').type(password);
cy.get('input[name="site_name"]').type("Epic Team");
cy.contains("Next").click();
// Database
cy.contains("I'll add my data later").click();
// Data Preferences
cy.contains("Allow Metabase to anonymously collect usage events")
.parents(".Form-field")
.find("a")
.click();
cy.contains("Next").click();
function makeUserObject(name, groupIds) {
return {
first_name: USERS[name].first_name,
last_name: USERS[name].last_name,
email: USERS[name].username,
password: USERS[name].password,
group_ids: groupIds,
};
}
cy.contains("Take me to Metabase").click();
function setup() {
cy.request("GET", "/api/session/properties").then(({ body: properties }) => {
cy.request("POST", "/api/setup", {
token: properties["setup-token"],
user: makeUserObject("admin"),
prefs: {
site_name: "Epic Team",
allow_tracking: false,
},
database: null,
});
});
}
function updateSettings() {
cy.visit("/admin/settings/public_sharing");
cy.contains("Disabled")
.prev()
.click();
cy.contains("Saved");
cy.visit("/admin/settings/embedding_in_other_applications");
cy.contains(/Enable/).click();
cy.contains("Saved");
cy.request("PUT", "/api/setting/enable-public-sharing", { value: true });
cy.request("PUT", "/api/setting/enable-embedding", { value: true });
cy.request("PUT", "/api/setting/embedding-secret-key", {
value: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
});
// update the Sample db connection string so it is valid in both CI and locally
cy.visit("/admin/databases/1");
cy.contains("Connection String")
.next()
.type("{SelectAll}./resources/sample-dataset.db;USER=GUEST;PASSWORD=guest");
cy.contains("Save").click();
cy.contains("Successfully saved!");
cy.request("GET", "/api/database/1").then(response => {
response.body.details.db =
"./resources/sample-dataset.db;USER=GUEST;PASSWORD=guest";
cy.request("PUT", "/api/database/1", response.body);
});
}
function addUser() {
cy.visit("/admin/people");
cy.contains("Add someone").click();
const ALL_USERS_GROUP = 1;
const COLLECTION_GROUP = 4;
const DATA_GROUP = 5;
const typeFieldInModal = (label, text) =>
cy
.get(".ModalContent")
.contains(label)
.next()
.type(text);
function addUsersAndGroups() {
// groups
cy.request("POST", "/api/permissions/group", { name: "collection" }); // 4
cy.request("POST", "/api/permissions/group", { name: "data" }); // 5
typeFieldInModal("First name", "Robert");
typeFieldInModal("Last name", "Tableton");
const { username, password } = NORMAL_USER_CREDS;
typeFieldInModal("Email", username);
cy.contains("Create").click();
cy.contains("Show").click();
cy.contains("Temporary Password")
.parent()
.next()
.invoke("val")
.as("tempPassword");
// Log out of the admin account, so we can change the password
cy.contains("Done").click();
cy.get(".Icon-gear")
.last()
.click();
cy.contains("Sign out").click();
// On logout, the signin page briefly flashes before the app reloads and
// displays it permanently. We need to wait for that reload so we don't start
// typing too soon.
cy.wait(1000);
// log into the normal user account using the temp password
cy.contains("Email address")
.next()
.type(username);
cy.get("@tempPassword").then(t =>
cy
.contains("Password")
.next()
.type(t),
// additional users
cy.request(
"POST",
"/api/user",
makeUserObject("normal", [ALL_USERS_GROUP, COLLECTION_GROUP, DATA_GROUP]),
);
cy.get(".Button").click();
// go to update password form
cy.get(".Icon-gear").click();
cy.contains("Account settings").click();
cy.contains("Password").click();
// update password
cy.get("@tempPassword").then(t =>
cy.get(`input[name="old_password"]`).type(t),
cy.request(
"POST",
"/api/user",
makeUserObject("nodata", [ALL_USERS_GROUP, COLLECTION_GROUP]),
);
cy.get(`input[name="password"]`)
.first()
.type(password);
cy.get(`input[name="password"]`)
.last()
.type(password);
cy.contains("Save").click();
cy.contains("Password updated successfully!");
cy.request(
"POST",
"/api/user",
makeUserObject("nocollection", [ALL_USERS_GROUP, DATA_GROUP]),
);
cy.request("POST", "/api/user", makeUserObject("none", [ALL_USERS_GROUP]));
// permissions
cy.request("PUT", "/api/permissions/graph", {
revision: 0,
groups: {
[ALL_USERS_GROUP]: { "1": { schemas: "none", native: "none" } },
[DATA_GROUP]: { "1": { schemas: "all", native: "write" } },
[COLLECTION_GROUP]: { "1": { schemas: "none", native: "none" } },
},
});
cy.request("PUT", "/api/collection/graph", {
revision: 0,
groups: {
[ALL_USERS_GROUP]: { root: "none" },
[DATA_GROUP]: { root: "none" },
[COLLECTION_GROUP]: { root: "write" },
},
});
}
function createQuestionAndDashboard() {
cy.visit("/question/new");
cy.contains("Simple question").click();
cy.contains("Orders").click();
cy.contains("Save").click(); // open save modal
modal()
.contains(/^Save$/)
.click(); // save question
// question 1: Orders
cy.request("POST", "/api/card", {
name: "Orders",
display: "table",
visualization_settings: {},
dataset_query: { database: 1, query: { "source-table": 2 }, type: "query" },
});
cy.visit("/question/new");
cy.contains("Simple question").click();
cy.contains("Sample Dataset").click();
cy.contains("Orders").click();
cy.contains("37.65");
cy.contains("Summarize").click();
cy.contains("Done").click();
cy.contains("18,760");
cy.contains("Save").click(); // open save modal
modal()
.contains(/^Save$/)
.click(); // save question
cy.contains("Saved!");
// question 2: Orders, Count
cy.request("POST", "/api/card", {
name: "Orders, Count",
display: "table",
visualization_settings: {},
dataset_query: {
database: 1,
query: { "source-table": 2, aggregation: [["count"]] },
type: "query",
},
});
// add to a dashboard
cy.contains("Yes please!").click();
cy.contains("Create a new dashboard").click();
cy.contains("Name")
.next()
.type("orders in a dashboard");
modal()
.contains("Create")
.click();
cy.contains("You are editing a dashboard");
cy.contains("Save").click();
cy.get(".EditHeader").should("have.length", 0);
// dashboard 1: Orders in a dashboard
cy.request("POST", "/api/dashboard", { name: "Orders in a dashboard" });
cy.request("POST", `/api/dashboard/1/cards`, { cardId: 1 });
// dismiss the "it's ok to play around" modal
cy.request("PUT", "/api/user/1/qbnewb", {});
......
This diff is collapsed.
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment