diff --git a/packages/browser-tests/cypress.config.js b/packages/browser-tests/cypress.config.js
index ff336654d..af92810a5 100644
--- a/packages/browser-tests/cypress.config.js
+++ b/packages/browser-tests/cypress.config.js
@@ -26,6 +26,13 @@ module.exports = defineConfig({
return launchOptions;
});
+
+ on("task", {
+ log(message) {
+ console.log(message);
+ return null;
+ },
+ });
},
},
});
diff --git a/packages/browser-tests/cypress/commands.js b/packages/browser-tests/cypress/commands.js
index b5091713a..96dec7d64 100644
--- a/packages/browser-tests/cypress/commands.js
+++ b/packages/browser-tests/cypress/commands.js
@@ -9,7 +9,9 @@ addMatchImageSnapshotCommand({
blackout: [".notifications", 'button[class*="BuildVersion"'],
});
-const ctrlOrCmd = Cypress.platform === "darwin" ? "{cmd}" : "{ctrl}";
+const { ctrlOrCmd, escapeRegExp } = require("./utils");
+
+const baseUrl = "http://localhost:9999";
before(() => {
Cypress.on("uncaught:exception", (err) => {
@@ -33,10 +35,32 @@ beforeEach(() => {
req.reply("{}");
}
);
+
+ cy.intercept(
+ {
+ method: "POST",
+ url: "/**",
+ hostname: "fara.questdb.io",
+ },
+ (req) => {
+ req.reply("{}");
+ }
+ ).as("addTelemetry");
+
+ cy.intercept(
+ {
+ method: "POST",
+ url: "/**",
+ hostname: "alurin.questdb.io",
+ },
+ (req) => {
+ req.reply("{}");
+ }
+ ).as("addTelemetry");
cy.intercept(
{
method: "GET",
- url: "/news",
+ url: "/api/news*",
hostname: "cloud.questdb.com",
},
(req) => {
@@ -67,7 +91,8 @@ Cypress.Commands.add("typeQuery", (query) =>
Cypress.Commands.add("runLine", () => {
cy.intercept("/exec*").as("exec");
- return cy.typeQuery(`${ctrlOrCmd}{enter}`).wait("@exec");
+ cy.typeQuery(`${ctrlOrCmd}{enter}`);
+ cy.wait("@exec");
});
Cypress.Commands.add("clickRun", () => {
@@ -146,3 +171,18 @@ Cypress.Commands.add("getCollapsedNotifications", () =>
Cypress.Commands.add("getExpandedNotifications", () =>
cy.get('[data-hook="notifications-expanded"]')
);
+
+Cypress.Commands.add("interceptQuery", (query, alias, response) => {
+ cy.intercept(
+ {
+ method: "GET",
+ url: new RegExp(
+ `^${escapeRegExp(baseUrl)}\/exec.*query=${encodeURIComponent(
+ escapeRegExp(query)
+ )}`,
+ "gmi"
+ ),
+ },
+ response
+ ).as(alias);
+});
diff --git a/packages/browser-tests/cypress/integration/console/editor.spec.js b/packages/browser-tests/cypress/integration/console/editor.spec.js
index d303e2e7c..db1733744 100644
--- a/packages/browser-tests/cypress/integration/console/editor.spec.js
+++ b/packages/browser-tests/cypress/integration/console/editor.spec.js
@@ -158,7 +158,8 @@ describe("&query URL param", () => {
it("should not append query if it already exists in editor", () => {
const query = "select x\nfrom long_sequence(1);\n\n-- a\n-- b\n-- c";
- cy.typeQuery(query).clickRun();
+ cy.typeQuery(query);
+ cy.clickRun();
cy.visit(`${baseUrl}?query=${encodeURIComponent(query)}&executeQuery=true`);
cy.getEditorContent().should("be.visible");
cy.getEditorContent().should("have.value", query);
@@ -166,7 +167,8 @@ describe("&query URL param", () => {
it("should append query and scroll to it", () => {
cy.typeQuery("select x from long_sequence(1);");
- cy.typeQuery("\n".repeat(20)).clickRun(); // take space so that query is not visible later, save by running
+ cy.typeQuery("\n".repeat(20));
+ cy.clickRun(); // take space so that query is not visible later, save by running
const appendedQuery = "-- hello world";
cy.visit(`${baseUrl}?query=${encodeURIComponent(appendedQuery)}`);
@@ -179,10 +181,8 @@ describe("&query URL param", () => {
describe("autocomplete", () => {
before(() => {
+ cy.visit(baseUrl);
cy.getEditorContent().should("be.visible");
- ["my_secrets", "my_secrets2", "my_publics"].forEach((table) => {
- cy.typeQuery(`drop table if exists "${table}"`).runLine().clearEditor();
- });
[
'create table "my_publics" ("public" string);',
// We're creating another table with the same column name.
@@ -202,10 +202,8 @@ describe("autocomplete", () => {
});
it("should work when provided table name doesn't exist", () => {
- cy.typeQuery("select * from teletubies")
- .getAutocomplete()
- .should("not.be.visible")
- .clearEditor();
+ cy.typeQuery("select * from teletubies");
+ cy.getAutocomplete().should("not.be.visible").clearEditor();
cy.matchImageSnapshot();
});
@@ -261,14 +259,16 @@ describe("errors", () => {
it("should mark '(200000)' as error", () => {
const query = `create table test (\ncol symbol index CAPACITY (200000)`;
- cy.typeQuery(query).runLine();
+ cy.typeQuery(query);
+ cy.runLine();
cy.matchErrorMarkerPosition({ left: 237, width: 67 });
cy.matchImageSnapshot();
});
it("should mark date position as error", () => {
const query = `select * from long_sequence(1) where cast(x as timestamp) = '2012-04-12T12:00:00A'`;
- cy.typeQuery(query).runLine();
+ cy.typeQuery(query);
+ cy.runLine();
cy.matchErrorMarkerPosition({ left: 506, width: 42 });
cy.getCollapsedNotifications().should("contain", "Invalid date");
@@ -290,7 +290,8 @@ describe("running query with F9", () => {
cy.F9();
cy.getGridRow(0).should("contain", "1");
cy.clearEditor();
- cy.typeQuery(`select * from long_sequence(2);{leftArrow}`).F9();
+ cy.typeQuery(`select * from long_sequence(2);{leftArrow}`);
+ cy.F9();
cy.getGridRow(1).should("contain", "2");
});
diff --git a/packages/browser-tests/cypress/integration/console/grid.spec.js b/packages/browser-tests/cypress/integration/console/grid.spec.js
index 64d973aa1..837c478c6 100644
--- a/packages/browser-tests/cypress/integration/console/grid.spec.js
+++ b/packages/browser-tests/cypress/integration/console/grid.spec.js
@@ -1,23 +1,23 @@
///
-describe("questdb grid", () => {
- before(() => {
- cy.visit("http://localhost:9999");
- });
+const baseUrl = "http://localhost:9999";
+describe("questdb grid", () => {
beforeEach(() => {
+ cy.visit(baseUrl);
+ cy.getEditorContent().should("be.visible");
cy.clearEditor();
});
it("when results empty", () => {
- cy.typeQuery("select x from long_sequence(0)")
- .runLine()
- .getGridRows()
- .should("have.length", 0);
+ cy.typeQuery("select x from long_sequence(0)");
+ cy.runLine();
+ cy.getGridRows().should("have.length", 0);
});
it("when results have vertical scroll", () => {
- cy.typeQuery(`select x from long_sequence(100)`).runLine();
+ cy.typeQuery(`select x from long_sequence(100)`);
+ cy.runLine();
cy.wait(100);
cy.getGridRows()
@@ -35,7 +35,8 @@ describe("questdb grid", () => {
const rows = 1000;
const rowsPerPage = 128;
const rowHeight = 30;
- cy.typeQuery(`select x from long_sequence(${rows})`).runLine();
+ cy.typeQuery(`select x from long_sequence(${rows})`);
+ cy.runLine();
for (let i = 0; i < rows; i += rowsPerPage) {
cy.getGridViewport().scrollTo(0, i * rowHeight);
@@ -48,8 +49,10 @@ describe("questdb grid", () => {
cy.getGridViewport().scrollTo("bottom");
});
- it.only("copy cell into the clipboard", () => {
- cy.typeQuery("select x from long_sequence(10)").runLine();
- cy.getGridCol(0).type("{ctrl}c").should("have.class", "qg-c-active-pulse");
+ it("copy cell into the clipboard", () => {
+ cy.typeQuery("select x from long_sequence(10)");
+ cy.runLine();
+ cy.getGridCol(0).type("{ctrl}c");
+ cy.getGridCol(0).should("have.class", "qg-c-active-pulse");
});
});
diff --git a/packages/browser-tests/cypress/integration/console/telemetry.spec.js b/packages/browser-tests/cypress/integration/console/telemetry.spec.js
new file mode 100644
index 000000000..e30d7aaf9
--- /dev/null
+++ b/packages/browser-tests/cypress/integration/console/telemetry.spec.js
@@ -0,0 +1,75 @@
+///
+
+const baseUrl = "http://localhost:9999";
+
+const toggleTelemetry = (enabled) => {
+ // expected dataset format of the first row:
+ // [id, enabled, version, os, package]
+ cy.interceptQuery("telemetry_config LIMIT -1", "telemetryConfig", (req) => {
+ return req.continue((res) => {
+ // enable telemetry to kick start the process on the client side
+ res.body.dataset[0][1] = enabled;
+ return res;
+ });
+ });
+};
+
+describe("telemetry config", () => {
+ beforeEach(() => {
+ toggleTelemetry(true);
+ cy.visit(baseUrl);
+ });
+
+ it("should get telemetry config", () => {
+ cy.wait("@telemetryConfig").then(({ response }) => {
+ const columnNames = response.body.columns.map((c) => c.name);
+ expect(response.statusCode).to.equal(200);
+ ["id", "enabled", "version", "os", "package"].forEach((name) => {
+ expect(columnNames).to.include(name);
+ });
+ expect(response.body.dataset[0][0]).to.be.string;
+ expect(response.body.dataset[0][1]).to.satisfy(
+ (v) => typeof v === "boolean"
+ );
+ expect(response.body.dataset[0][2]).to.be.string;
+ expect(response.body.dataset[0][3]).to.be.string;
+ expect(typeof response.body.dataset[0][4]).to.satisfy(
+ (v) => v === null || typeof v === "string"
+ );
+ });
+ });
+});
+
+describe("telemetry disabled", () => {
+ beforeEach(() => {
+ toggleTelemetry(false);
+ cy.visit(baseUrl);
+ });
+
+ it("should not start telemetry when disabled", () => {
+ cy.wait("@telemetryConfig").then(({ response }) => {
+ cy.intercept("@addTelemetry").then((interception) => {
+ expect(interception).to.be.null;
+ });
+ });
+ });
+});
+
+describe("telemetry enabled", () => {
+ beforeEach(() => {
+ toggleTelemetry(true);
+ cy.visit(baseUrl);
+ });
+
+ it("should start telemetry when enabled", () => {
+ cy.wait("@telemetryConfig").then(({ response }) => {
+ cy.wait("@addTelemetry").then(({ request }) => {
+ const payload = JSON.parse(request.body);
+ expect(payload.id).to.equal(response.body.dataset[0][0]);
+ expect(payload.version).to.equal(response.body.dataset[0][2]);
+ expect(payload.os).to.equal(response.body.dataset[0][3]);
+ expect(payload.package).to.equal(response.body.dataset[0][4]);
+ });
+ });
+ });
+});
diff --git a/packages/browser-tests/cypress/utils.js b/packages/browser-tests/cypress/utils.js
new file mode 100644
index 000000000..58e1f145f
--- /dev/null
+++ b/packages/browser-tests/cypress/utils.js
@@ -0,0 +1,5 @@
+exports.ctrlOrCmd = Cypress.platform === "darwin" ? "{cmd}" : "{ctrl}";
+
+exports.escapeRegExp = (string) => {
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+};
diff --git a/packages/browser-tests/questdb b/packages/browser-tests/questdb
index 03345738a..05c8dbc31 160000
--- a/packages/browser-tests/questdb
+++ b/packages/browser-tests/questdb
@@ -1 +1 @@
-Subproject commit 03345738a1bbcc0689ea2c56742a03abc4220182
+Subproject commit 05c8dbc31a45318e72dda1b15f9fceb8f85f82ea
diff --git a/packages/web-console/serve-dist.js b/packages/web-console/serve-dist.js
index 31f148352..87f5e00f0 100644
--- a/packages/web-console/serve-dist.js
+++ b/packages/web-console/serve-dist.js
@@ -7,7 +7,10 @@ const server = http.createServer((req, res) => {
const { method } = req
const urlData = url.parse(req.url)
- if (urlData.pathname.startsWith("/exec")) {
+ if (
+ urlData.pathname.startsWith("/exec") ||
+ urlData.pathname.startsWith("/settings")
+ ) {
// proxy /exec requests to localhost:9000
const options = {
hostname: "localhost",
diff --git a/packages/web-console/src/scenes/Editor/Monaco/index.tsx b/packages/web-console/src/scenes/Editor/Monaco/index.tsx
index 13e454c6d..99765ccd2 100644
--- a/packages/web-console/src/scenes/Editor/Monaco/index.tsx
+++ b/packages/web-console/src/scenes/Editor/Monaco/index.tsx
@@ -298,7 +298,10 @@ const MonacoEditor = () => {
renderLineMarkings(monacoRef.current, editorRef?.current)
}
- if (result.type === QuestDB.Type.DDL || result.type === QuestDB.Type.DML) {
+ if (
+ result.type === QuestDB.Type.DDL ||
+ result.type === QuestDB.Type.DML
+ ) {
dispatch(
actions.query.addNotification({
content: (
diff --git a/packages/web-console/src/scenes/Editor/Monaco/utils.ts b/packages/web-console/src/scenes/Editor/Monaco/utils.ts
index ea2c7546b..7b0c03e71 100644
--- a/packages/web-console/src/scenes/Editor/Monaco/utils.ts
+++ b/packages/web-console/src/scenes/Editor/Monaco/utils.ts
@@ -443,6 +443,7 @@ export const appendQuery = (
if (model) {
const position = editor.getPosition()
+ const lineCount = model.getLineCount()
if (position) {
const newQueryLines = query.split("\n")
@@ -490,7 +491,7 @@ export const appendQuery = (
editor.focus()
if (options.appendAt === "end") {
- editor.revealLine(model.getLineCount())
+ editor.revealLine(lineCount)
}
}
}