diff --git a/.babelrc b/.babelrc index d71a945b42ee2f5ce98ffedd50a8f391b4d47ac1..8d1f42041c21a72e2d6bc945d149d64720addc27 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,5 @@ { - "plugins": ["transform-flow-strip-types", "transform-decorators-legacy"], + "plugins": ["transform-flow-strip-types", "add-react-displayname", "transform-decorators-legacy"], "presets": ["es2015", "stage-0", "react"], "env": { "development": { diff --git a/.flowconfig b/.flowconfig index e27b923c11166afeb5834d3fbb0feb9aa99cd540..6e79a51cb877b77d17a3eab77a6c2d246e08f9fa 100644 --- a/.flowconfig +++ b/.flowconfig @@ -17,7 +17,7 @@ esproposal.decorators=ignore esproposal.class_static_fields=enable esproposal.class_instance_fields=enable suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe -module.name_mapper='.*\(.css\)' -> 'CSSModule' +module.name_mapper='.*\(\.css\)' -> 'CSSModule' module.system=haste strip_root=true module.file_ext=.js diff --git a/frontend/interfaces/webdriver.js b/frontend/interfaces/webdriver.js deleted file mode 100644 index c3516aeee83431ecbd17f11618d2b6c302f9f1c3..0000000000000000000000000000000000000000 --- a/frontend/interfaces/webdriver.js +++ /dev/null @@ -1,35 +0,0 @@ - -declare module "selenium-webdriver" { - - declare class WebDriver { - get(url: string): Promise<void>; - wait(condition: Condition|Function, timeout: ?number): WebElementPromise; - findElement(selector: By): WebElementPromise; - deleteAllCookies(): Promise<void>; - getCurrentUrl(): Promise<string>; - } - - declare class WebElement { - findElement(selector: By): WebElementPromise; - click(): Promise<void>; - sendKeys(keys: string): Promise<void>; - clear(): Promise<void>; - getText(): Promise<string>; - getAttribute(attribute: string): Promise<string>; - } - - declare class WebElementPromise extends WebElement { - } - - declare class Condition { - } - - declare class By { - static css(selector: string): By; - static xpath(selector: string): By; - } - - declare class until { - static elementLocated(selector: By): Condition; - } -} diff --git a/frontend/src/metabase/query_builder/components/VisualizationSettings.jsx b/frontend/src/metabase/query_builder/components/VisualizationSettings.jsx index 8ea7b0a136679edff6cb965d3cae992b63aa454f..04224216d2446991741d391f9536383f3ab026ff 100644 --- a/frontend/src/metabase/query_builder/components/VisualizationSettings.jsx +++ b/frontend/src/metabase/query_builder/components/VisualizationSettings.jsx @@ -40,7 +40,7 @@ export default class VisualizationSettings extends React.Component { name={CardVisualization.iconName} size={12} /> - {CardVisualization.displayName} + {CardVisualization.uiName} <Icon className="ml1" name="chevrondown" size={8} /> </span> ); @@ -73,7 +73,7 @@ export default class VisualizationSettings extends React.Component { onClick={this.setDisplay.bind(null, vizType)} > <Icon name={viz.iconName} size={12} /> - <span className="ml1">{viz.displayName}</span> + <span className="ml1">{viz.uiName}</span> </li> )} </ul> diff --git a/frontend/src/metabase/visualizations/AreaChart.jsx b/frontend/src/metabase/visualizations/AreaChart.jsx index c9c08e1ebf74eea07f0caaef9cce8bd45062f2f2..2d1d2e4c07c134321e07dd989ba9649094e3bca3 100644 --- a/frontend/src/metabase/visualizations/AreaChart.jsx +++ b/frontend/src/metabase/visualizations/AreaChart.jsx @@ -3,7 +3,7 @@ import React, { Component, PropTypes } from "react"; import LineAreaBarChart from "./components/LineAreaBarChart.jsx"; export default class AreaChart extends LineAreaBarChart { - static displayName = "Area"; + static uiName = "Area"; static identifier = "area"; static iconName = "area"; static noun = "area chart"; diff --git a/frontend/src/metabase/visualizations/BarChart.jsx b/frontend/src/metabase/visualizations/BarChart.jsx index f267dc73b80e60a685e16bce392d9c99ede36a74..c00633c36caf7308b99c6b24e0a97def5a32d8ea 100644 --- a/frontend/src/metabase/visualizations/BarChart.jsx +++ b/frontend/src/metabase/visualizations/BarChart.jsx @@ -3,7 +3,7 @@ import React, { Component, PropTypes } from "react"; import LineAreaBarChart from "./components/LineAreaBarChart.jsx"; export default class BarChart extends LineAreaBarChart { - static displayName = "Bar"; + static uiName = "Bar"; static identifier = "bar"; static iconName = "bar"; static noun = "bar chart"; diff --git a/frontend/src/metabase/visualizations/Funnel.jsx b/frontend/src/metabase/visualizations/Funnel.jsx index 7dcbbd2e1a263a0a838d65446964b1db0fb2437b..16d0c8a00a42a3fbafcc005f627d1b2dabe32caa 100644 --- a/frontend/src/metabase/visualizations/Funnel.jsx +++ b/frontend/src/metabase/visualizations/Funnel.jsx @@ -8,7 +8,7 @@ import { getSettings } from "metabase/lib/visualization_settings"; import i from "icepick"; export default class Funnel extends Component { - static displayName = "Funnel"; + static uiName = "Funnel"; static identifier = "funnel"; static iconName = "funnel"; diff --git a/frontend/src/metabase/visualizations/LineChart.jsx b/frontend/src/metabase/visualizations/LineChart.jsx index 45406129e96378c1a65c7b14db0eb5436199d53b..88a0dba4c4178c87bca2205bd13104773edc5729 100644 --- a/frontend/src/metabase/visualizations/LineChart.jsx +++ b/frontend/src/metabase/visualizations/LineChart.jsx @@ -3,7 +3,7 @@ import React, { Component, PropTypes } from "react"; import LineAreaBarChart from "./components/LineAreaBarChart.jsx"; export default class LineChart extends LineAreaBarChart { - static displayName = "Line"; + static uiName = "Line"; static identifier = "line"; static iconName = "line"; static noun = "line chart"; diff --git a/frontend/src/metabase/visualizations/Map.jsx b/frontend/src/metabase/visualizations/Map.jsx index 1fa4d557f498a03856c3435494f7e9e1ae77df32..85d4d4c42595091b898e27e2275d791991668967 100644 --- a/frontend/src/metabase/visualizations/Map.jsx +++ b/frontend/src/metabase/visualizations/Map.jsx @@ -6,7 +6,7 @@ import PinMap from "./PinMap.jsx"; import { ChartSettingsError } from "metabase/visualizations/lib/errors"; export default class Map extends Component { - static displayName = "Map"; + static uiName = "Map"; static identifier = "map"; static iconName = "pinmap"; diff --git a/frontend/src/metabase/visualizations/PieChart.jsx b/frontend/src/metabase/visualizations/PieChart.jsx index 4490dc65694a03c06ddea37dc3844e9a04b79974..91b0eff55127c1d43cf14ed9ab0369c4612276a9 100644 --- a/frontend/src/metabase/visualizations/PieChart.jsx +++ b/frontend/src/metabase/visualizations/PieChart.jsx @@ -27,7 +27,7 @@ const OTHER_SLICE_MIN_PERCENTAGE = 0.003; const PERCENT_REGEX = /percent/i; export default class PieChart extends Component { - static displayName = "Pie"; + static uiName = "Pie"; static identifier = "pie"; static iconName = "pie"; diff --git a/frontend/src/metabase/visualizations/PinMap.jsx b/frontend/src/metabase/visualizations/PinMap.jsx index a43c7025d1d211c2794ba96a2e9db8f8c98ba021..6874ac44beb46c3905cd9d3259bf94697b95ffbe 100644 --- a/frontend/src/metabase/visualizations/PinMap.jsx +++ b/frontend/src/metabase/visualizations/PinMap.jsx @@ -17,7 +17,7 @@ const MAP_COMPONENTS_BY_TYPE = { } export default class PinMap extends Component { - static displayName = "Pin Map"; + static uiName = "Pin Map"; static identifier = "pin_map"; static iconName = "pinmap"; diff --git a/frontend/src/metabase/visualizations/Progress.jsx b/frontend/src/metabase/visualizations/Progress.jsx index 0377fbc4719f0f49e6a08d51bc833253d106deac..ad1e11cabb0fc466fc6aeb0e7f328e72b3469c75 100644 --- a/frontend/src/metabase/visualizations/Progress.jsx +++ b/frontend/src/metabase/visualizations/Progress.jsx @@ -11,7 +11,7 @@ import Color from "color"; const BORDER_RADIUS = 5; export default class Progress extends Component { - static displayName = "Progress"; + static uiName = "Progress"; static identifier = "progress"; static iconName = "progress"; diff --git a/frontend/src/metabase/visualizations/Scalar.jsx b/frontend/src/metabase/visualizations/Scalar.jsx index 3b08089c20d4eb17c3b59efe753a31cedf55e63b..ff1ffe39c7d3ea886d034bb06ecdee0096b59883 100644 --- a/frontend/src/metabase/visualizations/Scalar.jsx +++ b/frontend/src/metabase/visualizations/Scalar.jsx @@ -14,7 +14,7 @@ import i from "icepick"; import d3 from "d3"; export default class Scalar extends Component { - static displayName = "Number"; + static uiName = "Number"; static identifier = "scalar"; static iconName = "number"; diff --git a/frontend/src/metabase/visualizations/ScatterPlot.jsx b/frontend/src/metabase/visualizations/ScatterPlot.jsx index 0f5af3c5857028dcf750249f9dba71e4d689cbdd..0f90ba6ad33bf25a9b4cb2ac168152b4b29033b6 100644 --- a/frontend/src/metabase/visualizations/ScatterPlot.jsx +++ b/frontend/src/metabase/visualizations/ScatterPlot.jsx @@ -3,7 +3,7 @@ import React, { Component, PropTypes } from "react"; import LineAreaBarChart from "./components/LineAreaBarChart.jsx"; export default class ScatterPlot extends LineAreaBarChart { - static displayName = "Scatter"; + static uiName = "Scatter"; static identifier = "scatter"; static iconName = "bubble"; static noun = "scatter plot"; diff --git a/frontend/src/metabase/visualizations/Table.jsx b/frontend/src/metabase/visualizations/Table.jsx index fa9c2708f758be12725500f054505f4fd2ef691a..51813928bcb11f2240703c4a2164b323afe6f804 100644 --- a/frontend/src/metabase/visualizations/Table.jsx +++ b/frontend/src/metabase/visualizations/Table.jsx @@ -7,7 +7,7 @@ import * as DataGrid from "metabase/lib/data_grid"; import _ from "underscore"; export default class Bar extends Component { - static displayName = "Table"; + static uiName = "Table"; static identifier = "table"; static iconName = "table"; diff --git a/frontend/src/metabase/visualizations/XKCDChart.jsx b/frontend/src/metabase/visualizations/XKCDChart.jsx index a8a61456dc9380662155493385fc9623dd242942..58a86f42c71c22de157d1ab72f9484fc4e8dbe4e 100644 --- a/frontend/src/metabase/visualizations/XKCDChart.jsx +++ b/frontend/src/metabase/visualizations/XKCDChart.jsx @@ -19,7 +19,7 @@ import "xkcdplot/humor-sans"; import cx from "classnames"; export default class XKCDChart extends Component { - static displayName = "XKCD" + static uiName = "XKCD" static identifier = "xkcd"; static iconName = "pinmap"; diff --git a/frontend/test/e2e/admin/settings.spec.js b/frontend/test/e2e/admin/settings.spec.js index 142fa205697f840b7255c9db1d6572d7dd8c9f2a..8960e4f39f5d578856abec63158b5b747e7341f5 100644 --- a/frontend/test/e2e/admin/settings.spec.js +++ b/frontend/test/e2e/admin/settings.spec.js @@ -13,17 +13,24 @@ describeE2E("admin/settings", () => { describe("admin settings", () => { it("should persist a setting", async () => { + // pick a random site name to try updating it to const siteName = "Metabase" + Math.random(); + // load the "general" pane of the admin settings await d.get(`${server.host}/admin/settings/general`); + // first just make sure the site name isn't already set (it shouldn't since we're using a random name) expect(await d.select(".SettingsInput").wait().attribute("value")).not.toBe(siteName); + // clear the site name input, send the keys corresponding to the site name, then blur to trigger the update await d.select(".SettingsInput").wait().clear().sendKeys(siteName).blur(); + // wait for the loading indicator to show success await d.select(".SaveStatus.text-success").wait(); + // reload the page await d.get(`${server.host}/admin/settings/general`); + // verify the site name value was persisted expect(await d.select(".SettingsInput").wait().attribute("value")).toBe(siteName); }); }); diff --git a/frontend/test/e2e/query_builder/query_builder.spec.js b/frontend/test/e2e/query_builder/query_builder.spec.js index 98f8ad03932ea2b7242396b650303898f4c0fefd..6e4106bd8722b4ed842020f59468a80d715e3e2a 100644 --- a/frontend/test/e2e/query_builder/query_builder.spec.js +++ b/frontend/test/e2e/query_builder/query_builder.spec.js @@ -1,8 +1,5 @@ import { - waitForElementRemoved, - waitForElementAndClick, - waitForElementAndSendKeys, screenshot, describeE2E, ensureLoggedIn @@ -17,132 +14,134 @@ describeE2E("query_builder", () => { describe("tables", () => { it("should allow users to create pivot tables", async () => { - await driver.get(`${server.host}/q`); + // load the query builder and screenshot blank + await d.get("/q"); + await d.screenshot("screenshots/qb-initial.png"); - await screenshot(driver, "screenshots/qb-initial.png"); + // pick the orders table (assumes database is already selected, i.e. there's only 1 database) + await d.select("#TablePicker .List-item a:contains(Orders)").wait().click(); - await waitForElementAndClick(driver, "#TablePicker .List-item:first-child>a"); + await d.select(":react(AggregationWidget)").wait().click(); - await waitForElementAndClick(driver, "#Query-section-aggregation"); - await waitForElementAndClick(driver, "#AggregationPopover .List-item:nth-child(2)>a"); + await d.select("#AggregationPopover .List-item:nth-child(2)>a").wait().click(); - await waitForElementAndClick(driver, ".Query-section.Query-section-breakout #BreakoutWidget"); - await waitForElementAndClick(driver, "#BreakoutPopover .List-section:nth-child(3) .List-section-header"); - await waitForElementAndClick(driver, "#BreakoutPopover .List-item:nth-child(12)>a"); + await d.select(".Query-section.Query-section-breakout #BreakoutWidget").wait().click(); + await d.select("#BreakoutPopover .List-section:nth-child(3) .List-section-header").wait().click(); + await d.select("#BreakoutPopover .List-item:nth-child(12)>a").wait().click(); - await waitForElementAndClick(driver, ".Query-section.Query-section-breakout #BreakoutWidget .AddButton"); - await waitForElementAndClick(driver, "#BreakoutPopover .List-item:first-child .Field-extra>a"); - await waitForElementAndClick(driver, "#TimeGroupingPopover .List-item:nth-child(4)>a"); + await d.select(".Query-section.Query-section-breakout #BreakoutWidget .AddButton").wait().click(); + await d.select("#BreakoutPopover .List-item:first-child .Field-extra>a").wait().click(); + await d.select("#TimeGroupingPopover .List-item:nth-child(4)>a").wait().click(); - await waitForElementAndClick(driver, ".Button.RunButton"); + await d.select(".Button.RunButton").wait().click(); - await waitForElementRemoved(driver, ".Loading", 20000); - await screenshot(driver, "screenshots/qb-pivot-table.png"); + await d.select(".Loading").waitRemoved(20000); + await d.screenshot("screenshots/qb-pivot-table.png"); // save question - await waitForElementAndClick(driver, ".Header-buttonSection:first-child"); - await waitForElementAndSendKeys(driver, "#SaveQuestionModal input[name='name']", 'Pivot Table'); - await waitForElementAndClick(driver, "#SaveQuestionModal .Button.Button--primary"); + await d.select(".Header-buttonSection:first-child").wait().click(); + await d.select("#SaveQuestionModal input[name='name']").wait().sendKeys("Pivot Table"); + await d.select("#SaveQuestionModal .Button.Button--primary").wait().click().waitRemoved(); // wait for the modal to be removed // add to new dashboard - await waitForElementAndClick(driver, "#QuestionSavedModal .Button.Button--primary"); - await waitForElementAndSendKeys(driver, "#CreateDashboardModal input[name='name']", 'Main Dashboard'); - await waitForElementAndClick(driver, "#CreateDashboardModal .Button.Button--primary"); + await d.select("#QuestionSavedModal .Button.Button--primary").wait().click(); + await d.select("#CreateDashboardModal input[name='name']").wait().sendKeys("Main Dashboard"); + await d.select("#CreateDashboardModal .Button.Button--primary").wait().click().waitRemoved(); // wait for the modal to be removed // save dashboard - await waitForElementAndClick(driver, ".EditHeader .Button.Button--primary"); - await waitForElementRemoved(driver, ".EditHeader"); + await d.select(".EditHeader .Button.Button--primary").wait().click(); + await d.select(".EditHeader").waitRemoved(); }); }); describe("charts", () => { xit("should allow users to create line charts", async () => { - await driver.get(`${server.host}/q`); + await d.get("/q"); // select orders table - await waitForElementAndClick(driver, "#TablePicker .List-item:first-child>a"); + await d.select("#TablePicker .List-item:first-child>a").wait().click(); // select filters - await waitForElementAndClick(driver, ".GuiBuilder-filtered-by .Query-section:not(.disabled) a"); + await d.select(".GuiBuilder-filtered-by .Query-section:not(.disabled) a").wait().click(); - await waitForElementAndClick(driver, "#FilterPopover .List-item:first-child>a"); + await d.select("#FilterPopover .List-item:first-child>a").wait().click(); - await waitForElementAndClick(driver, ".Button[data-ui-tag='relative-date-shortcut-this-year']"); - await waitForElementAndClick(driver, ".Button[data-ui-tag='add-filter']:not(.disabled)"); + await d.select(".Button[data-ui-tag='relative-date-shortcut-this-year']").wait().click(); + await d.select(".Button[data-ui-tag='add-filter']:not(.disabled)").wait().click(); // select aggregations - await waitForElementAndClick(driver, "#Query-section-aggregation"); - await waitForElementAndClick(driver, "#AggregationPopover .List-item:nth-child(2)>a"); + await d.select("#Query-section-aggregation").wait().click(); + await d.select("#AggregationPopover .List-item:nth-child(2)>a").wait().click(); // select breakouts - await waitForElementAndClick(driver, ".Query-section.Query-section-breakout>div"); + await d.select(".Query-section.Query-section-breakout>div").wait().click(); - await waitForElementAndClick(driver, "#BreakoutPopover .List-item:first-child .Field-extra>a"); - await waitForElementAndClick(driver, "#TimeGroupingPopover .List-item:nth-child(3)>a"); + await d.select("#BreakoutPopover .List-item:first-child .Field-extra>a").wait().click(); + await d.select("#TimeGroupingPopover .List-item:nth-child(3)>a").wait().click(); // run query - await waitForElementAndClick(driver, ".Button.RunButton"); + await d.select(".Button.RunButton").wait().click(); - await waitForElementAndClick(driver, "#VisualizationTrigger"); + await d.select("#VisualizationTrigger").wait().click(); // this step occassionally fails without the timeout - await driver.sleep(500); - await waitForElementAndClick(driver, "#VisualizationPopover li:nth-child(3)"); + await d.sleep(500); + await d.select("#VisualizationPopover li:nth-child(3)").wait().click(); await screenshot(driver, "screenshots/qb-line-chart.png"); // save question - await waitForElementAndClick(driver, ".Header-buttonSection:first-child"); - await waitForElementAndSendKeys(driver, "#SaveQuestionModal input[name='name']", 'Line Chart'); - await waitForElementAndClick(driver, "#SaveQuestionModal .Button.Button--primary"); + await d.select(".Header-buttonSection:first-child").wait().click(); + await d.select("#SaveQuestionModal input[name='name']").wait().sendKeys("Line Chart"); + await d.select("#SaveQuestionModal .Button.Button--primary").wait().click(); // add to existing dashboard - await driver.sleep(500); - await waitForElementAndClick(driver, "#QuestionSavedModal .Button.Button--primary"); - await waitForElementAndClick(driver, "#AddToDashSelectDashModal .SortableItemList-list li:first-child>a"); + await d.sleep(500); + await d.select("#QuestionSavedModal .Button.Button--primary").wait().click(); + await d.select("#AddToDashSelectDashModal .SortableItemList-list li:first-child>a").wait().click(); // save dashboard - await waitForElementAndClick(driver, ".EditHeader .Button.Button--primary"); - await waitForElementRemoved(driver, ".EditHeader"); + await d.select(".EditHeader .Button.Button--primary").wait().click(); + await d.select(".EditHeader").waitRemoved(); }); xit("should allow users to create bar charts", async () => { // load line chart - await driver.get(`${server.host}/card/2`); + await d.get("/card/2"); // dismiss saved questions modal - await waitForElementAndClick(driver, ".Modal .Button.Button--primary"); + await d.select(".Modal .Button.Button--primary").wait().click(); // change breakouts - await waitForElementAndClick(driver, ".View-section-breakout.SelectionModule"); + await d.select(".View-section-breakout.SelectionModule").wait().click(); - await waitForElementAndClick(driver, "#BreakoutPopover .List-item:first-child .Field-extra>a"); - await waitForElementAndClick(driver, "#TimeGroupingPopover .List-item:nth-child(4)>a"); + await d.select("#BreakoutPopover .List-item:first-child .Field-extra>a").wait().click(); + await d.select("#TimeGroupingPopover .List-item:nth-child(4)>a").wait().click(); // change visualization - await waitForElementAndClick(driver, "#VisualizationTrigger"); + await d.select("#VisualizationTrigger").wait().click(); // this step occassionally fails without the timeout - await driver.sleep(500); - await waitForElementAndClick(driver, "#VisualizationPopover li:nth-child(4)"); + await d.sleep(500); + await d.select("#VisualizationPopover li:nth-child(4)").wait().click(); // run query - await waitForElementAndClick(driver, ".Button.RunButton"); - await waitForElementRemoved(driver, ".Loading", 20000); + await d.select(".Button.RunButton").wait().click(); + await d.select(".Loading").waitRemoved(20000); await screenshot(driver, "screenshots/qb-bar-chart.png"); // save question - await waitForElementAndClick(driver, ".Header-buttonSection:first-child"); - await waitForElementAndSendKeys(driver, "#SaveQuestionModal input[name='name']", 'Bar Chart'); - await waitForElementAndClick(driver, "#SaveQuestionModal .Button.Button--primary"); + await d.select(".Header-buttonSection:first-child").wait().click(); + await d.select("#SaveQuestionModal input[name='name']").wait().sendKeys("Bar Chart"); + await d.select("#SaveQuestionModal .Button.Button--primary").wait().click(); // add to existing dashboard - await driver.sleep(500); - await waitForElementAndClick(driver, "#QuestionSavedModal .Button.Button--primary"); - await waitForElementAndClick(driver, "#AddToDashSelectDashModal .SortableItemList-list li:first-child>a"); + await d.sleep(500); + await d.select("#QuestionSavedModal .Button.Button--primary").wait().click(); + await d.select("#AddToDashSelectDashModal .SortableItemList-list li:first-child>a").wait().click(); // save dashboard - await waitForElementAndClick(driver, ".EditHeader .Button.Button--primary"); - await waitForElementRemoved(driver, ".EditHeader"); + await d.select(".EditHeader .Button.Button--primary").wait().click(); + await d.select(".EditHeader").waitRemoved(); }); }); }); diff --git a/frontend/test/e2e/support/utils.js b/frontend/test/e2e/support/utils.js index feb21b0e4911546ada35d4f3ce8e964e53f13179..43e69a6a08da7b135dbfa4601d5eceed597c01a0 100644 --- a/frontend/test/e2e/support/utils.js +++ b/frontend/test/e2e/support/utils.js @@ -131,7 +131,7 @@ export const screenshotFailures = async (driver) => { export const getJson = async (driver, url) => { await driver.get(url); try { - let source = await driver.getPageSource(); + let source = await driver.findElement(By.tagName("body")).getText(); return JSON.parse(source); } catch (e) { return null; @@ -143,17 +143,45 @@ export const checkLoggedIn = async (server, driver, email) => { return currentUser && currentUser.email === email; } +const getSessionId = (server, email) => { + server.sessions = server.sessions || {}; + return server.sessions[email]; +} +const setSessionId = (server, email, sessionId) => { + server.sessions = server.sessions || {}; + server.sessions[email] = sessionId; +} + export const ensureLoggedIn = async (server, driver, email, password) => { if (await checkLoggedIn(server, driver, email)) { - console.log("already logged in"); + console.log("LOGIN: already logged in"); return; } - console.log("logging in"); + const sessionId = getSessionId(server, email); + if (sessionId != null) { + console.log("LOGIN: trying previous session"); + await driver.get(`${server.host}/`); + await driver.manage().deleteAllCookies(); + await driver.manage().addCookie("metabase.SESSION_ID", sessionId); + await driver.get(`${server.host}/`); + if (await checkLoggedIn(server, driver, email)) { + console.log("LOGIN: cached session succeeded"); + return; + } else { + console.log("LOGIN: cached session failed"); + setSessionId(server, email, null); + } + } + + console.log("LOGIN: logging in manually"); await driver.get(`${server.host}/`); await driver.manage().deleteAllCookies(); await driver.get(`${server.host}/`); await loginMetabase(driver, email, password); await waitForUrl(driver, `${server.host}/`); + + const sessionCookie = await driver.manage().getCookie("metabase.SESSION_ID"); + setSessionId(server, email, sessionCookie.value); } export const loginMetabase = async (driver, email, password) => { @@ -189,7 +217,9 @@ export const describeE2E = (name, options, describeCallback) => { ]); global.driver = webdriver.driver; - global.d = new Driver(webdriver.driver); + global.d = new Driver(webdriver.driver, { + base: server.host + }); global.server = server; await driver.get(`${server.host}/`); diff --git a/package.json b/package.json index 6d7eea067d579cc7c1606ee0511f06a2cb214343..be7de7d26273112b7ffda25646e326ebbe667da9 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "babel-core": "^6.20.0", "babel-eslint": "^6.1.2", "babel-loader": "^6.2.4", + "babel-plugin-add-react-displayname": "^0.0.4", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-plugin-transform-flow-strip-types": "^6.8.0", "babel-preset-es2015": "^6.6.0", @@ -120,7 +121,7 @@ "selenium-webdriver": "^2.53.3", "style-loader": "^0.13.0", "unused-files-webpack-plugin": "^2.0.2", - "webchauffeur": "^1.0.1", + "webchauffeur": "^1.2.0", "webpack": "^1.12.14", "webpack-dev-server": "^1.14.0", "webpack-hot-middleware": "^2.10.0", diff --git a/yarn.lock b/yarn.lock index e7c214409b55cbc7c6eec48389af307c22c48acf..32129ff29969b22d4e209b9772cbf0eabb5afc7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -160,6 +160,10 @@ amdefine@>=0.0.4: version "1.0.0" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.0.tgz#fd17474700cb5cc9c2b709f0be9d23ce3c198c33" +annotate-react-dom@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/annotate-react-dom/-/annotate-react-dom-1.1.0.tgz#607c14d2565198d4bf365f6f05c60a61ba939a16" + ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" @@ -571,6 +575,10 @@ babel-messages@^6.8.0: dependencies: babel-runtime "^6.0.0" +babel-plugin-add-react-displayname@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.4.tgz#bc2a74bcbee6e505025b3352fea85ee7bc4c6f7c" + babel-plugin-check-es2015-constants@^6.3.13: version "6.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.8.0.tgz#dbf024c32ed37bfda8dee1e76da02386a8d26fe7" @@ -1218,6 +1226,10 @@ bluebird@^3.0.5, bluebird@^3.3.3, bluebird@^3.3.4, bluebird@^3.4.1: version "3.4.6" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" +bo-selector@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/bo-selector/-/bo-selector-0.0.10.tgz#9816dcb00adf374ea87941a863b2acfc026afa3e" + body-parser@^1.12.4: version "1.15.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.15.2.tgz#d7578cf4f1d11d5f6ea804cef35dc7a7ff6dae67" @@ -1842,6 +1854,13 @@ css-selector-tokenizer@^0.6.0: fastparse "^1.1.1" regexpu-core "^1.0.0" +css-to-xpath@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/css-to-xpath/-/css-to-xpath-0.1.0.tgz#ac0d1c26cef023f7bd8cf2e1fc1f77134bc70c47" + dependencies: + bo-selector "0.0.10" + xpath-builder "0.0.7" + css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" @@ -4415,6 +4434,14 @@ mz@^2.3.1: object-assign "^4.0.1" thenify-all "^1.0.0" +mz@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.6.0.tgz#c8b8521d958df0a4f2768025db69c719ee4ef1ce" + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + nan@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232" @@ -6991,10 +7018,13 @@ watchpack@^0.2.1: chokidar "^1.0.0" graceful-fs "^4.1.2" -webchauffeur: - version "1.0.1" - resolved "https://registry.yarnpkg.com/webchauffeur/-/webchauffeur-1.0.1.tgz#ed9d4ae28bb3a2e87d755b2d470c535e39cf30e4" +webchauffeur@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/webchauffeur/-/webchauffeur-1.2.0.tgz#d03d7f38d336c2ae55099d978adabc0e75a50d6f" dependencies: + annotate-react-dom "^1.1.0" + css-to-xpath "^0.1.0" + mz "^2.6.0" promise-chain-decorator "^1.2.0" webpack-core@~0.6.0: @@ -7187,6 +7217,10 @@ xmlhttprequest-ssl@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.1.tgz#3b7741fea4a86675976e908d296d4445961faa67" +xpath-builder@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/xpath-builder/-/xpath-builder-0.0.7.tgz#67d6bbc3f6a320ec317e3e6368c5706b6111deec" + xtend@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"