From 523ace9ddd28ac3941c8f7e840cd4c6c3bae7ff5 Mon Sep 17 00:00:00 2001 From: Renku Bot Date: Wed, 18 Jun 2025 14:01:53 +0000 Subject: [PATCH 1/4] chore: create release 2.3.0 From a47742274f53d62c48281ee98762515733e75df2 Mon Sep 17 00:00:00 2001 From: Andrea Cordoba Date: Thu, 19 Jun 2025 15:11:59 +0200 Subject: [PATCH 2/4] refactor: update project creation (v1) to use API instead of form submission --- .../cypress/support/commands/projects.ts | 72 +++++++++++-------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/cypress-tests/cypress/support/commands/projects.ts b/cypress-tests/cypress/support/commands/projects.ts index 1678725fe1..faac308f61 100644 --- a/cypress-tests/cypress/support/commands/projects.ts +++ b/cypress-tests/cypress/support/commands/projects.ts @@ -69,6 +69,47 @@ interface NewProjectProps extends ProjectIdentifier { visibility?: "public" | "private" | "internal"; } +interface NewProjectTemplate { + namespace: string; + slug: string; + project_id: string; + name: string; +} + +function createProjectV1API(newProjectProps: NewProjectProps) { + const newProjectBody = { + identifier: newProjectProps.templateName, + project_name: newProjectProps.name, + project_namespace: newProjectProps.namespace, + project_repository: "https://gitlab.dev.renku.ch", + ref: "0.9.0", + url: "https://github.com/SwissDataScienceCenter/renku-project-template" + } + cy.request({ + method: "POST", + url: "api/renku/templates.create_project", + body: newProjectBody, + headers: { + "Content-Type": "application/json", + }, + }).then((responseProject: Cypress.Response) => { + const project = responseProject.body; + const updateProjectBody = { + id: project.namespace + "/" + project.slug, + name: project.name, + visibility: newProjectProps.visibility ?? "private", + } + return cy.request({ + method: "PUT", + url: "api/projects/" + project.namespace + "/" + project.slug, + body: updateProjectBody, + headers: { + "Content-Type": "application/json", + }, + }); + }); +} + function createProjectIfMissing(newProjectProps: NewProjectProps) { const namespace = newProjectProps.namespace ?? Cypress.env("TEST_USERNAME"); const slug = encodeURIComponent(`${namespace}/${newProjectProps.name}`); @@ -76,36 +117,11 @@ function createProjectIfMissing(newProjectProps: NewProjectProps) { failOnStatusCode: false, method: "GET", url: `/ui-server/api/projects/${slug}`, - }).then((response) => { + }).then(async (response) => { if (response.status != 200) { - cy.visit("/v1/projects/new"); - cy.getDataCy("field-group-title") - .should("be.visible") - .clear() - .type(newProjectProps.name); - if (newProjectProps.namespace) { - cy.get("#namespace-input") - .should("be.visible") - .clear() - .type(newProjectProps.namespace); - } - - if (newProjectProps.templateName) - cy.contains(newProjectProps.templateName).should("be.visible").click(); - - if (newProjectProps.visibility) { - cy.getDataCy(`visibility-${newProjectProps.visibility}`) - .should("be.visible") - .click(); - } - // The button may take some time before it is clickable - cy.get("[data-cy=create-project-button]", { timeout: TIMEOUTS.vlong }) - .should("be.enabled") - .click(); - } - else { - cy.visit(`projects/${namespace}/${newProjectProps.name}`); + await createProjectV1API(newProjectProps); } + cy.visit(`projects/${ namespace }/${ newProjectProps.name }`); cy.url({ timeout: TIMEOUTS.vlong }).should( "contain", newProjectProps.name.toLowerCase(), From 2208f32967ca965035bab4fb4723302c649da8c5 Mon Sep 17 00:00:00 2001 From: Andrea Cordoba Date: Sat, 21 Jun 2025 21:37:57 +0200 Subject: [PATCH 3/4] add getTemplates to createProjectV1API --- .../cypress/support/commands/projects.ts | 90 +++++++++++++------ 1 file changed, 61 insertions(+), 29 deletions(-) diff --git a/cypress-tests/cypress/support/commands/projects.ts b/cypress-tests/cypress/support/commands/projects.ts index faac308f61..eb9e331883 100644 --- a/cypress-tests/cypress/support/commands/projects.ts +++ b/cypress-tests/cypress/support/commands/projects.ts @@ -70,49 +70,81 @@ interface NewProjectProps extends ProjectIdentifier { } interface NewProjectTemplate { - namespace: string; - slug: string; - project_id: string; - name: string; + result: { + namespace: string; + slug: string; + project_id: string; + name: string; + } } +interface Templates { + templates: { + folder: string; + name: string; + }[]; +} function createProjectV1API(newProjectProps: NewProjectProps) { - const newProjectBody = { - identifier: newProjectProps.templateName, - project_name: newProjectProps.name, - project_namespace: newProjectProps.namespace, - project_repository: "https://gitlab.dev.renku.ch", - ref: "0.9.0", - url: "https://github.com/SwissDataScienceCenter/renku-project-template" - } - cy.request({ - method: "POST", - url: "api/renku/templates.create_project", - body: newProjectBody, - headers: { - "Content-Type": "application/json", - }, - }).then((responseProject: Cypress.Response) => { - const project = responseProject.body; - const updateProjectBody = { - id: project.namespace + "/" + project.slug, - name: project.name, - visibility: newProjectProps.visibility ?? "private", + const resTemplates = getTemplates(); + resTemplates.then((response) => { + console.log("Templates fetched from manifest:", response.result); + if (response.result.templates.length === 0) { + throw new Error("No templates found in the manifest"); } - return cy.request({ - method: "PUT", - url: "api/projects/" + project.namespace + "/" + project.slug, - body: updateProjectBody, + const newProjectBody = { + identifier: response.result.templates[0].folder ?? "python", + project_name: newProjectProps.name, + project_namespace: newProjectProps.namespace, + project_repository: "https://gitlab.dev.renku.ch", + ref: "0.9.0", + url: "https://github.com/SwissDataScienceCenter/renku-project-template", + }; + cy.request({ + method: "POST", + url: "api/renku/templates.create_project", + body: newProjectBody, headers: { "Content-Type": "application/json", }, + }).then((responseProject: Cypress.Response) => { + const project = responseProject.body.result; + const identifier = encodeURIComponent(project.namespace + "/" + project.slug); + const updateProjectBody = { + id: identifier, + visibility: newProjectProps.visibility ?? "private", + }; + return cy.request({ + method: "PUT", + url: "api/projects/" + identifier, + body: updateProjectBody, + headers: { + "Content-Type": "application/json", + }, + }); }); }); } +function getTemplates() { + return cy.request({ + method: "GET", + url: "api/renku/templates.read_manifest?url=https%3A%2F%2Fgithub.com%2FSwissDataScienceCenter%2Frenku-project-template", + headers: { + "Content-Type": "application/json", + }, + }).then((response) => { + if (response.status !== 200) { + throw new Error(`Failed to fetch templates: ${response.statusText}`); + } + return response.body; + }); +} + + function createProjectIfMissing(newProjectProps: NewProjectProps) { const namespace = newProjectProps.namespace ?? Cypress.env("TEST_USERNAME"); const slug = encodeURIComponent(`${namespace}/${newProjectProps.name}`); + cy.request({ failOnStatusCode: false, method: "GET", From bd54351f19dc4cf2d432d9b976d18cbb6b593c6f Mon Sep 17 00:00:00 2001 From: Andrea Cordoba Date: Mon, 23 Jun 2025 13:38:12 +0200 Subject: [PATCH 4/4] fix e2e test --- cypress-tests/cypress/support/commands/datasets.ts | 4 ++++ cypress-tests/cypress/support/commands/projects.ts | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cypress-tests/cypress/support/commands/datasets.ts b/cypress-tests/cypress/support/commands/datasets.ts index d265ecb10e..61a3c64505 100644 --- a/cypress-tests/cypress/support/commands/datasets.ts +++ b/cypress-tests/cypress/support/commands/datasets.ts @@ -1,6 +1,7 @@ import { v4 as uuidv4 } from "uuid"; import { ProjectIdentifier } from "./projects"; +import { TIMEOUTS } from "../../../config"; export type DatasetIdentifier = ProjectIdentifier & { datasetName: string; @@ -29,6 +30,9 @@ function searchForDataset(name: string, shouldExist = true) { .scrollIntoView() .should("be.visible") .check(); + + // wait until search indexing is done, sometimes it takes some more seconds + cy.wait(TIMEOUTS.standard); // eslint-disable-line cypress/no-unnecessary-waiting cy.get("input[placeholder='Search...']") .should("be.visible") .type(name) diff --git a/cypress-tests/cypress/support/commands/projects.ts b/cypress-tests/cypress/support/commands/projects.ts index eb9e331883..e21fb781fd 100644 --- a/cypress-tests/cypress/support/commands/projects.ts +++ b/cypress-tests/cypress/support/commands/projects.ts @@ -87,7 +87,6 @@ interface Templates { function createProjectV1API(newProjectProps: NewProjectProps) { const resTemplates = getTemplates(); resTemplates.then((response) => { - console.log("Templates fetched from manifest:", response.result); if (response.result.templates.length === 0) { throw new Error("No templates found in the manifest"); } @@ -111,7 +110,7 @@ function createProjectV1API(newProjectProps: NewProjectProps) { const identifier = encodeURIComponent(project.namespace + "/" + project.slug); const updateProjectBody = { id: identifier, - visibility: newProjectProps.visibility ?? "private", + visibility: newProjectProps.visibility ?? "public", }; return cy.request({ method: "PUT", @@ -320,6 +319,13 @@ function waitMetadataIndexing(justTriggered = true, goToSettings = true) { } if (goToSettings) cy.getProjectSection("Settings").click(); cy.getDataCy("kg-status-section-open").click(); + // Check if the update button is present and click it + cy.getDataCy("kg-status-section-action-button").then(($el) => { + const updateButton = $el.find("#button-update-projectKnowledgeGraph"); + if (updateButton.length) { + cy.wrap(updateButton).click(); + } + }); cy.getDataCy("project-settings-knowledge-graph") .contains("Everything indexed", { timeout: TIMEOUTS.vlong }) .should("be.visible");