diff --git a/.gitignore b/.gitignore index 8edf733f8..67358d3dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *-init.clj *.jar *.penpot +*.orig .calva .clj-kondo .cpcache diff --git a/frontend/cypress/fixtures/fonts/Viafont.otf b/frontend/cypress/fixtures/fonts/Viafont.otf new file mode 100644 index 000000000..40a8470b4 Binary files /dev/null and b/frontend/cypress/fixtures/fonts/Viafont.otf differ diff --git a/frontend/cypress/fixtures/fonts/blkchcry.ttf b/frontend/cypress/fixtures/fonts/blkchcry.ttf new file mode 100644 index 000000000..cca50917c Binary files /dev/null and b/frontend/cypress/fixtures/fonts/blkchcry.ttf differ diff --git a/frontend/cypress/fixtures/validuser-sample.json b/frontend/cypress/fixtures/validuser-sample.json index 6bf5894a9..7301b2b7d 100644 --- a/frontend/cypress/fixtures/validuser-sample.json +++ b/frontend/cypress/fixtures/validuser-sample.json @@ -1,4 +1,5 @@ { "email": "validuser@penpot.app", - "password": "password" + "password": "password", + "team": "test team" } \ No newline at end of file diff --git a/frontend/cypress/integration/01-auth/create-account.spec.js b/frontend/cypress/integration/01-auth/create-account.spec.js index 84d390013..3cc2b5269 100644 --- a/frontend/cypress/integration/01-auth/create-account.spec.js +++ b/frontend/cypress/integration/01-auth/create-account.spec.js @@ -23,6 +23,17 @@ describe("account creation", () => { cy.getBySel("register-form-submit").should("exist"); }); + it("create an account", () => { + let email = "mail" + Date.now() +"@mail.com"; + cy.get("#email").type(email); + cy.get("#password").type("anewpassword"); + cy.get("input[type=submit]").click(); + cy.getBySel("register-title").should("exist"); + cy.get("#fullname").type("Test user") + cy.get("input[type=submit]").click(); + cy.get(".dashboard-layout").should("exist"); + }); + it("create an account of an existent email fails", () => { cy.get("#email").type(validUser.email); cy.get("#password").type("anewpassword"); diff --git a/frontend/cypress/integration/02-onboarding/onboarding-options.spec.js b/frontend/cypress/integration/02-onboarding/onboarding-options.spec.js new file mode 100644 index 000000000..1db5d14d1 --- /dev/null +++ b/frontend/cypress/integration/02-onboarding/onboarding-options.spec.js @@ -0,0 +1,71 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + describe("onboarding options solo or team", () => { + beforeEach(() => { + cy.demoLogin(); + cy.get(".modal-right button").click(); + cy.get(".onboarding button").click(); + cy.get(".onboarding .skip").click(); + }); + + it("choose solo option", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("fly-solo-op").click(); + cy.getBySel("onboarding-templates-title").should("exist"); + }); + + it("choose team option and cancel", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("team-up-button").click(); + cy.getBySel("onboarding-choice-team-up").should("exist"); + cy.get("button").click(); + cy.getBySel("onboarding-welcome-title").should("exist"); + }); + + it("choose team option, set team name and cancel", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("team-up-button").click(); + cy.getBySel("onboarding-choice-team-up").should("exist"); + cy.get("#name").type("test team"); + cy.get("input[type=submit]").first().click(); + cy.get("#email").should("exist"); + cy.get("button").click(); + cy.getBySel("onboarding-welcome-title").should("exist"); + }); + + it("choose team option, set team name and skip", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("team-up-button").click(); + cy.getBySel("onboarding-choice-team-up").should("exist"); + cy.get("#name").type("test team"); + cy.get("input[type=submit]").first().click(); + cy.get("#email").should("exist"); + cy.get(".skip-action").click(); + cy.getBySel("onboarding-templates-title").should("exist"); + }); + + it("choose team option, set team name and invite", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("team-up-button").click(); + cy.getBySel("onboarding-choice-team-up").should("exist"); + cy.get("#name").type("test team"); + cy.get("input[type=submit]").first().click(); + cy.get("#email").should("exist"); + cy.get("#email").type("test@test.com"); + cy.get("input[type=submit]").first().click(); + cy.getBySel("onboarding-templates-title").should("exist"); + }); + + + + }); + + \ No newline at end of file diff --git a/frontend/cypress/integration/03-dashboard/files.spec.js b/frontend/cypress/integration/03-dashboard/files.spec.js new file mode 100644 index 000000000..ff385c7a0 --- /dev/null +++ b/frontend/cypress/integration/03-dashboard/files.spec.js @@ -0,0 +1,489 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + import { + + createProject, + deleteFirstProject, + deleteFirstFile, + createFile + +} from '../../support/utils.js'; + + + + describe("files", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + createProject("test project" + Date.now()); + }); + + }); + + afterEach(() => { + //cleanup + deleteFirstProject(); + }); + + + it("can create a new file", () => { + cy.get(".grid-item").then((files) => { + cy.get('.project').first().find("[data-test=project-new-file]").click(); + cy.get("#workspace").should("exist"); + cy.get(".project-tree").should("contain", "New File"); + + //Go back + cy.get(".main-icon a").click(); + cy.get(".grid-item").should('have.length', files.length + 1); + }) + + }) + + it("can create a new file inside a project", () => { + cy.get(".project").first().find("h2").click(); + cy.get(".grid-item").should('have.length', 0); + createFile(); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can create a new file inside a project with shortcut", () => { + cy.get(".project").first().find("h2").click(); + cy.get(".grid-item").should('have.length', 0); + cy.get("body").type("+"); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can delete a file inside a project", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + cy.get(".grid-item").should('have.length', 1); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.accept-button').click(); + cy.get(".grid-item").should('have.length', 0); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can cancel a file deletion inside a project", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + cy.get(".grid-item").should('have.length', 1); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.cancel-button').click(); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can delete a file outside a project", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + //Go back + cy.get(".recent-projects").click(); + + cy.get(".grid-item").then((files) => { + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.accept-button').click(); + cy.get(".grid-item").should('have.length', files.length-1); + }); + }) + + it("can cancel a file deletion outside a project", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + //Go back + cy.get(".recent-projects").click(); + + cy.get(".grid-item").then((files) => { + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.cancel-button').click(); + cy.get(".grid-item").should('have.length', files.length); + }); + }) + + it("can rename a file", () => { + const fileName = "test file " + Date.now(); + + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-rename").click(); + cy.get(".edit-wrapper").should("exist"); + cy.get(".edit-wrapper").type(fileName + "{enter}"); + + cy.get(".grid-item").first().should("contain", fileName); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can duplicate a file", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get(".grid-item").should('have.length', 1); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-duplicate").click(); + cy.get(".grid-item").should('have.length', 2); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can move a file to another project", () => { + const projectToMoveName = "test project to move " + Date.now(); + const fileName = "test file " + Date.now(); + + createProject(projectToMoveName); + cy.get(".project").eq(1).find("h2").click(); + createFile(fileName); + + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.wait(500); + cy.getBySel("file-move-to").click(); + + cy.get('a').contains(projectToMoveName).click(); + + cy.getBySel("project-title").should("contain", projectToMoveName); + cy.get(".grid-item").should('have.length', 1); + cy.get(".grid-item").first().should("contain", fileName); + + + //Go back and cleanup: delete project + cy.get(".recent-projects").click(); + deleteFirstProject(); + }); + + + it("can move a file to another team", () => { + const fileName = "test file " + Date.now(); + cy.get(".project").first().find("h2").click(); + createFile(fileName); + + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.wait(500); + cy.getBySel("file-move-to").click(); + + cy.getBySel("move-to-other-team").click(); + cy.fixture('validuser.json').then((user) => { + cy.get('a').contains(user.team).click(); + cy.get('a').contains("Drafts").click(); + cy.get(".current-team").should("contain", user.team); + cy.get(".dashboard-title").should("contain", "Drafts"); + cy.get(".grid-item").first().should("contain", fileName); + }); + + + //cleanup + deleteFirstFile(); + cy.get(".current-team").click(); + cy.get(".team-name").contains("Your Penpot").click(); + }); + + + it("can make a file a shared library", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get(".icon-library").should('have.length', 0); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-add-shared").click(); + cy.get(".accept-button").click(); + cy.get(".icon-library").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can cancel make a file a shared library", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get(".icon-library").should('have.length', 0); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-add-shared").click(); + cy.get(".modal-close-button").click(); + cy.get(".icon-library").should('have.length', 0); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can remove a file as shared library", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-add-shared").click(); + cy.get(".accept-button").click(); + cy.get(".icon-library").should('have.length', 1); + + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-del-shared").click(); + cy.get(".accept-button").click(); + cy.get(".icon-library").should('have.length', 0); + + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can cancel remove a file as shared library", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-add-shared").click(); + cy.get(".accept-button").click(); + cy.get(".icon-library").should('have.length', 1); + + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-del-shared").click(); + cy.get(".modal-close-button").click(); + cy.get(".icon-library").should('have.length', 1); + + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can search for a file", () => { + const fileName = "test file " + Date.now(); + + cy.get(".project").first().find("h2").click(); + createFile(fileName); + + cy.get("#search-input").type("bad name"); + cy.get(".grid-item").should('have.length', 0); + + cy.get("#search-input").clear().type(fileName); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can multiselect files", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".selected").should('have.length', 0); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".selected").should('have.length', 1); + + cy.get(".grid-item").eq(2).click({shiftKey: true}); + cy.get(".selected").should('have.length', 2); + + cy.get(".grid-item").eq(1).click({shiftKey: true}); + cy.get(".selected").should('have.length', 3); + + cy.get(".grid-item").eq(1).click({shiftKey: true}); + cy.get(".selected").should('have.length', 2); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can delete multiselected files", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + cy.get(".grid-item").should('have.length', 3); + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("delete-multi-files").should("contain", "Delete 2 files"); + cy.getBySel("delete-multi-files").click(); + cy.get('.accept-button').click(); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can cancel delete multiselected files", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + cy.get(".grid-item").should('have.length', 3); + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("delete-multi-files").should("contain", "Delete 2 files"); + cy.getBySel("delete-multi-files").click(); + cy.get('.cancel-button').click(); + cy.get(".grid-item").should('have.length', 3); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can duplicate multiselected files", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + cy.get(".grid-item").should('have.length', 3); + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("duplicate-multi").should("contain", "Duplicate 2 files"); + cy.getBySel("duplicate-multi").click(); + cy.get(".grid-item").should('have.length', 5); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can move multiselected files to another project", () => { + const projectToMoveName = "test project to move " + Date.now(); + createProject(projectToMoveName); + + cy.get(".project").eq(1).find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("move-to-multi").should("contain", "Move 2 files to"); + cy.getBySel("move-to-multi").click(); + cy.get('a').contains(projectToMoveName).click(); + + cy.getBySel("project-title").should("contain", projectToMoveName); + cy.get(".grid-item").should('have.length', 2); + + + //Go back + cy.get(".recent-projects").click(); + deleteFirstProject(); + }) + + + it("can move multiselected files to another team", () => { + const fileName1 = "test file " + Date.now(); + const fileName2 = "test file " + Date.now(); + const fileName3 = "test file " + Date.now(); + + cy.get(".project").first().find("h2").click(); + createFile(fileName1) + createFile(fileName2) + createFile(fileName3) + + + //multiselect first and third file + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("move-to-multi").should("contain", "Move 2 files to"); + cy.getBySel("move-to-multi").click(); + cy.getBySel("move-to-other-team").click(); + cy.fixture('validuser.json').then((user) => { + cy.get('a').contains(user.team).click(); + cy.get('a').contains("Drafts").click(); + cy.get(".current-team").should("contain", user.team); + cy.get(".dashboard-title").should("contain", "Drafts"); + cy.get(".grid-item").eq(0).should("contain", fileName1); + cy.get(".grid-item").eq(1).should("contain", fileName2); + }); + + + //cleanup + deleteFirstFile() + deleteFirstFile() + cy.get(".current-team").click(); + cy.get(".team-name").contains("Your Penpot").click(); + }) + + }); + + diff --git a/frontend/cypress/integration/03-dashboard/misc.spec.js b/frontend/cypress/integration/03-dashboard/misc.spec.js new file mode 100644 index 000000000..840d5b296 --- /dev/null +++ b/frontend/cypress/integration/03-dashboard/misc.spec.js @@ -0,0 +1,150 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + import { + + deleteFirstFile, + deleteFirstFont + +} from '../../support/utils.js'; + + + +describe("comments", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + + }); + + it("can open and close comments popup", () => { + cy.get(".comments-section").should("not.exist"); + cy.getBySel("open-comments").click(); + cy.get(".comments-section").should("exist"); + cy.getBySel("open-comments").click(); + cy.get(".comments-section").should("not.exist"); + }); + +}); + +describe("import and export", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + }); + + it("can export a file", () => { + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-export").click(); + cy.get('.icon-tick').should("exist"); + }); + + it("can import a file", () => { + cy.get(".grid-item").then((files) => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.getBySel("file-import").click(); + + cy.uploadBinaryFile("input[type=file]", "test-file-import.penpot"); + + cy.get(".accept-button").should('not.be.disabled'); + cy.get(".accept-button").click(); + cy.get(".accept-button").should('not.be.disabled'); + cy.get(".accept-button").click(); + cy.get(".grid-item").should('have.length', files.length+1); + }); + + //cleanup + deleteFirstFile() ; + }) + +}) + +describe("release notes", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + }); + + it("can show release notes", () => { + cy.get(".profile").click(); + cy.getBySel("profile-profile-opt").click(); + cy.get(".onboarding").should("not.exist"); + cy.getBySel("release-notes").click(); + cy.get(".onboarding").should("exist"); + }); +}); + +describe("fonts", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + }); + + it("can upload a font file", () => { + cy.getBySel("fonts").click(); + cy.get(".font-item").should('have.length', 0); + cy.uploadBinaryFile("#font-upload", "fonts/Viafont.otf"); + cy.get(".upload-button").click(); + cy.get(".font-item").should('have.length', 1); + + //cleanup + deleteFirstFont(); + + }); + + it("can upload multiple font files", () => { + cy.getBySel("fonts").click(); + cy.get(".font-item").should('have.length', 0); + cy.uploadBinaryFile("#font-upload", "fonts/Viafont.otf"); + cy.uploadBinaryFile("#font-upload", "fonts/blkchcry.ttf"); + cy.getBySel("upload-all").click(); + cy.get(".font-item").should('have.length', 2); + + //cleanup + deleteFirstFont(); + deleteFirstFont(); + }); + + it("can dismiss multiple font files", () => { + cy.getBySel("fonts").click(); + cy.get(".font-item").should('have.length', 0); + cy.uploadBinaryFile("#font-upload", "fonts/Viafont.otf"); + cy.uploadBinaryFile("#font-upload", "fonts/blkchcry.ttf"); + cy.getBySel("dismiss-all").click(); + cy.get(".font-item").should('have.length', 0); + }); + + it("can rename a font", () => { + const fontName = "test font " + Date.now(); + + //Upload a font + cy.getBySel("fonts").click(); + cy.uploadBinaryFile("#font-upload", "fonts/Viafont.otf"); + cy.get(".upload-button").click(); + cy.get(".font-item").should('have.length', 1); + + //Rename font + cy.get(".font-item .options").first().click(); + cy.getBySel("font-edit").click(); + cy.get(".dashboard-installed-fonts input[value=Viafont]").type(fontName+"{enter}"); + cy.get(".dashboard-installed-fonts").should("contain", fontName); + + //cleanup + deleteFirstFont(); + + }); +}); \ No newline at end of file diff --git a/frontend/cypress/integration/03-dashboard/projects.spec.js b/frontend/cypress/integration/03-dashboard/projects.spec.js new file mode 100644 index 000000000..d2b94aac5 --- /dev/null +++ b/frontend/cypress/integration/03-dashboard/projects.spec.js @@ -0,0 +1,153 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + import { + + createProject, + deleteFirstProject + +} from '../../support/utils.js'; + + + + describe("projects", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password) + }); + + }); + + it("displays the projects page", () => { + cy.get(".dashboard-title").should("contain", "Projects"); + }); + + it("can create a new project", () => { + let projectName = "test project " + Date.now(); + cy.get(".project").then((projects) => { + cy.getBySel("new-project-button").click(); + cy.get('.project').should('have.length', projects.length + 1); + cy.get('.project').first().find(".edit-wrapper").type(projectName + "{enter}") + cy.get('.project').first().find("h2").should("contain", projectName); + + //cleanup: delete project + deleteFirstProject(); + }) + + }) + + it("can rename a project", () => { + let projectName = "test project " + Date.now(); + let projectName2 = "renamed project " + Date.now(); + + createProject(projectName); + + cy.get('.project').first().find("h2").should("contain", projectName); + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.get('.project').first().find("[data-test=project-rename]").click(); + cy.get('.project').first().find(".edit-wrapper").type(projectName2 + "{enter}") + cy.get('.project').first().find("h2").should("contain", projectName2) + + //cleanup: delete project + deleteFirstProject(); + }); + + it("can delete a project", () => { + createProject(); + cy.get(".project").then((projects) => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.wait(500); + cy.getBySel("project-delete").click(); + cy.wait(500); + cy.get('.accept-button').click(); + cy.wait(500); + + cy.get('.project').should('have.length', projects.length - 1); + }) + }); + + it("can cancel the deletion of a project", () => { + createProject(); + cy.get(".project").then((projects) => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.wait(500); + cy.getBySel("project-delete").click(); + cy.wait(500); + cy.get('.cancel-button').click(); + cy.wait(500); + + cy.get('.project').should('have.length', projects.length); + + + //cleanup: delete project + deleteFirstProject(); + }) + }); + + it("can duplicate a project", () => { + let projectName = "test project " + Date.now(); + createProject(projectName); + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.wait(500); + cy.getBySel("project-duplicate").click(); + cy.getBySel("project-title").should("exist"); + cy.getBySel("project-title").should("contain", projectName+" ("); + + + //cleanup: delete project + cy.get(".recent-projects").click(); + deleteFirstProject(); + deleteFirstProject(); + }); + + + it("can move a project to a team", () => { + let projectName = "test project " + Date.now(); + createProject(projectName); + + cy.fixture('validuser.json').then((user) => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.get('.project').first().find("[data-test=project-move-to]").click(); + cy.get('a').contains(user.team).click(); + + cy.get(".current-team").should("contain", user.team); + cy.get(".project").first().should("contain", projectName); + + + //cleanup: delete project + deleteFirstProject(); + }); + }); + + + it("pin and unpin project to sidebar", () => { + let projectName = "test project " + Date.now(); + createProject(projectName); + + cy.get(".project").first().find(".icon-pin-fill").should("exist"); + cy.getBySel("pinned-projects").should("contain", projectName); + + //unpin + cy.get(".project").first().find(".pin-icon").click(); + cy.get(".project").first().find(".icon-pin-fill").should("not.exist"); + cy.getBySel("pinned-projects").should("not.contain", projectName); + + //pin + cy.get(".project").first().find(".pin-icon").click(); + cy.get(".project").first().find(".icon-pin-fill").should("exist"); + cy.getBySel("pinned-projects").should("contain", projectName); + + //cleanup: delete project + deleteFirstProject(); + }); + + }); + + diff --git a/frontend/cypress/integration/03-dashboard/teams.spec.js b/frontend/cypress/integration/03-dashboard/teams.spec.js new file mode 100644 index 000000000..98071e3d2 --- /dev/null +++ b/frontend/cypress/integration/03-dashboard/teams.spec.js @@ -0,0 +1,155 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + import { + + createTeam, + deleteCurrentTeam + + } from '../../support/utils.js'; + + + describe("teams", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + + }); + + it("can create a new team", () => { + const teamName = "test team " + Date.now(); + cy.get(".current-team").click(); + cy.getBySel("create-new-team").click(); + cy.get("#name").type(teamName); + cy.get("input[type=submit]").click(); + + cy.get(".current-team").should("contain", teamName); + + //cleanup + deleteCurrentTeam(); + + }) + + it("can cancel create a new team", () => { + cy.get(".current-team").click(); + cy.getBySel("create-new-team").click(); + cy.get(".modal-close-button").click(); + + cy.get(".current-team").should("contain", "Your Penpot"); + }) + + it("can delete a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("delete-team").click(); + cy.get(".accept-button").click(); + cy.get(".current-team").should("contain", "Your Penpot"); + }) + + it("can cancel the deletion of a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("delete-team").click(); + cy.get(".cancel-button").click(); + cy.get(".current-team").should("contain", teamName); + + + //cleanup + deleteCurrentTeam(); + }) + + it("can see the members page of a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("team-members").click(); + + cy.get(".dashboard-title").should("contain", "Members"); + cy.fixture('validuser.json').then((user) => { + cy.get(".dashboard-table").should("contain", user.email); + }); + + //cleanup + deleteCurrentTeam(); + }) + + it("can invite someone to a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("team-members").click(); + + cy.getBySel("invite-member").click(); + cy.get("#email").type("mail@mail.com"); + cy.get(".custom-select select").select("admin"); + cy.get("input[type=submit]").click(); + + cy.get(".success").should("exist"); + + //cleanup + deleteCurrentTeam(); + }) + + it("can see the settings page of a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("team-settings").click(); + + cy.get(".dashboard-title").should("contain", "Settings"); + + cy.get(".team-settings .name").should("contain", teamName); + + //cleanup + deleteCurrentTeam(); + }) + + it("can rename team", () => { + const teamName = "test team " + Date.now(); + const newTeamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("rename-team").click(); + cy.get("#name").type(newTeamName); + cy.get("input[type=submit]").click(); + + cy.get(".current-team").should("contain", newTeamName); + + //cleanup + deleteCurrentTeam(); + }) + + it("can cancel the rename of a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("rename-team").click(); + cy.get(".modal-close-button").click(); + + cy.get(".current-team").should("contain", teamName); + + //cleanup + deleteCurrentTeam(); + }) + + + + +}) \ No newline at end of file diff --git a/frontend/cypress/integration/03-projects/projects.spec.js b/frontend/cypress/integration/03-projects/projects.spec.js deleted file mode 100644 index 97743c085..000000000 --- a/frontend/cypress/integration/03-projects/projects.spec.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * Copyright (c) UXBOX Labs SL - */ - - "use strict"; - - describe("projects", () => { - beforeEach(() => { - cy.fixture('validuser.json').then((user) => { - cy.login(user.email, user.password) - }); - - }); - - it("displays the projects page", () => { - cy.get(".dashboard-title").should("exist"); - }); - - }); - - \ No newline at end of file diff --git a/frontend/cypress/integration/09-draw/draw-shapes.spec.js b/frontend/cypress/integration/09-draw/draw-shapes.spec.js index c9ab4784f..46e7970f7 100644 --- a/frontend/cypress/integration/09-draw/draw-shapes.spec.js +++ b/frontend/cypress/integration/09-draw/draw-shapes.spec.js @@ -18,48 +18,48 @@ describe("draw shapes", () => { }); it("draw an artboard", () => { - cy.get(".viewport-controls rect").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Artboard (A)']").click(); + cy.get(".render-shapes rect").should("not.exist"); + cy.getBySel("artboard-btn").click(); cy.drawInViewport(300, 300, 400, 450); - cy.get(".viewport-controls rect").first().as("artboard"); + cy.get(".render-shapes rect").first().as("artboard"); cy.get("@artboard").should("exist"); cy.get("@artboard").invoke("attr", "width").should("eq", "100"); cy.get("@artboard").invoke("attr", "height").should("eq", "150"); }); it("draw a square", () => { - cy.get(".viewport-controls rect").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Rectangle (R)']").click(); + cy.get(".render-shapes rect").should("not.exist"); + cy.getBySel("rect-btn").click(); cy.drawInViewport(300, 300, 400, 450); - cy.get(".viewport-controls rect").should("exist"); - cy.get(".viewport-controls rect") + cy.get(".render-shapes rect").should("exist"); + cy.get(".render-shapes rect") .invoke("attr", "width") .should("eq", "100"); - cy.get(".viewport-controls rect") + cy.get(".render-shapes rect") .invoke("attr", "height") .should("eq", "150"); }); it("draw an ellipse", () => { - cy.get(".viewport-controls ellipse").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Ellipse (E)']").click(); + cy.get(".render-shapes ellipse").should("not.exist"); + cy.getBySel("ellipse-btn").click(); cy.drawInViewport(300, 300, 400, 450); - cy.get(".viewport-controls ellipse").as("ellipse"); + cy.get(".render-shapes ellipse").as("ellipse"); cy.get("@ellipse").should("exist"); cy.get("@ellipse").invoke("attr", "rx").should("eq", "50"); cy.get("@ellipse").invoke("attr", "ry").should("eq", "75"); }); it("draw a curve", () => { - cy.get(".viewport-controls path").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Curve (Shift+C)']").click(); + cy.get(".render-shapes path").should("not.exist"); + cy.getBySel("curve-btn").click(); cy.drawMultiInViewport([ { x: 300, y: 300 }, { x: 350, y: 300 }, { x: 300, y: 350 }, { x: 400, y: 450 }, ]); - cy.get(".viewport-controls path").as("curve"); + cy.get(".render-shapes path").as("curve"); cy.get("@curve").should("exist"); cy.get("@curve") .invoke("attr", "d") @@ -67,8 +67,8 @@ describe("draw shapes", () => { }); it("draw a path", () => { - cy.get(".viewport-controls path").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Path (P)']").click(); + cy.get(".render-shapes path").should("not.exist"); + cy.getBySel("path-btn").click(); cy.clickMultiInViewport([ { x: 300, y: 300 }, { x: 350, y: 300 }, @@ -81,7 +81,7 @@ describe("draw shapes", () => { true ); cy.clickMultiInViewport([{ x: 300, y: 300 }]); - cy.get(".viewport-controls path").as("curve"); + cy.get(".render-shapes path").as("curve"); cy.get("@curve").should("exist"); cy.get("@curve") .invoke("attr", "d") diff --git a/frontend/cypress/support/commands.js b/frontend/cypress/support/commands.js index 9283cca5e..fa5cee741 100644 --- a/frontend/cypress/support/commands.js +++ b/frontend/cypress/support/commands.js @@ -88,7 +88,7 @@ Cypress.Commands.add('clickMultiInViewport', (coords) => { Cypress.Commands.add('clearViewport', () => { cy.get(".viewport-controls").type('{ctrl}a'); cy.get(".viewport-controls").type('{del}'); - cy.window().its("debug").invoke('reset_viewport') + cy.window().its("debug").invoke('reset_viewport'); }) Cypress.Commands.add('getBySel', (selector, ...args) => { @@ -97,4 +97,18 @@ Cypress.Commands.add('getBySel', (selector, ...args) => { Cypress.Commands.add('getBySelLike', (selector, ...args) => { return cy.get(`[data-test*=${selector}]`, ...args) -}) \ No newline at end of file +}) + +Cypress.Commands.add('uploadBinaryFile', (fileInputSelector, fileName) => { + cy.fixture(fileName, "binary") + .then(Cypress.Blob.binaryStringToBlob) + .then(fileContent => { + cy.get(fileInputSelector).attachFile({ + fileContent, + filePath: fileName, + encoding: 'utf-8', + lastModified: new Date().getTime() + }); + }); +}) + diff --git a/frontend/cypress/support/utils.js b/frontend/cypress/support/utils.js index ee5ea66af..595f71ced 100644 --- a/frontend/cypress/support/utils.js +++ b/frontend/cypress/support/utils.js @@ -5,8 +5,71 @@ export const checkOnboardingSlide = (number, checkSkip) => { cy.getBySel(`slide-${number}-btn`).should("exist"); cy.getBySel(`slide-${number}-btn`).click(); }; +export const deleteFirstProject = () => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.wait(500); + cy.get('.project').first().find("[data-test=project-delete]").click(); + cy.wait(500); + cy.get('.accept-button').click(); + } + + export const createProject = (projectName="") => { + cy.getBySel("new-project-button").click(); + cy.wait(500); + cy.get('.project').first().find(".edit-wrapper").type(projectName + "{enter}"); + cy.wait(500); + } + + + export const deleteFirstFile = () => { + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.accept-button').click(); + } + + + export const createFile = (fileName="", projectNum=0) => { + cy.getBySel("new-file").click(); + cy.wait(500); + if (fileName !=""){ + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-rename").click(); + cy.get(".edit-wrapper").type(fileName + "{enter}"); + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + } + } + + export const createTeam = (teamName) => { + cy.get(".current-team").click(); + cy.getBySel("create-new-team").click(); + cy.get("#name").type(teamName); + cy.get("input[type=submit]").click(); + cy.wait(500); + } + + export const deleteCurrentTeam = () => { + cy.get(".icon-actions").first().click(); + cy.getBySel("delete-team").click(); + cy.get(".accept-button").click(); + } + + + export const goToSlideByNumber = (number) => { cy.get(`.step-dots li:nth-child(${number})`).click(); cy.getBySel(`slide-${number -1}-btn`).should("exist"); +}; + +export const deleteFirstFont = () => { + cy.get(".font-item .options").first().click(); + cy.getBySel("font-delete").click(); + cy.get(".accept-button").click(); }; \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e48a53af4..2486f8650 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -32,7 +32,8 @@ }, "devDependencies": { "autoprefixer": "^10.4.1", - "cypress": "^9.2.1", + "cypress": "^9.3.1", + "cypress-file-upload": "^5.0.8", "gettext-parser": "^4.2.0", "gulp": "4.0.2", "gulp-concat": "^2.6.1", @@ -385,11 +386,10 @@ "license": "MIT" }, "node_modules/@types/sinonjs__fake-timers": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz", - "integrity": "sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==", - "dev": true, - "license": "MIT" + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true }, "node_modules/@types/sizzle": { "version": "2.3.3", @@ -2539,20 +2539,21 @@ "license": "MIT" }, "node_modules/cypress": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.2.1.tgz", - "integrity": "sha512-LVEe4yWCo4xO0Vd8iYjFHRyd5ulRvM56XqMgAdn05Qb9kJ6iJdO/MmjKD8gNd768698cp1FDuSmFQZHVZGk+Og==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz", + "integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==", "dev": true, "hasInstallScript": true, "dependencies": { "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", - "@types/sinonjs__fake-timers": "^6.0.2", + "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", - "bluebird": "3.7.2", + "bluebird": "^3.7.2", + "buffer": "^5.6.0", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -2594,6 +2595,42 @@ "node": ">=12.0.0" } }, + "node_modules/cypress-file-upload": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz", + "integrity": "sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==", + "dev": true, + "engines": { + "node": ">=8.2.1" + }, + "peerDependencies": { + "cypress": ">3.0.0" + } + }, + "node_modules/cypress/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/cypress/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12105,9 +12142,9 @@ "dev": true }, "@types/sinonjs__fake-timers": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz", - "integrity": "sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "dev": true }, "@types/sizzle": { @@ -13707,19 +13744,20 @@ "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" }, "cypress": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.2.1.tgz", - "integrity": "sha512-LVEe4yWCo4xO0Vd8iYjFHRyd5ulRvM56XqMgAdn05Qb9kJ6iJdO/MmjKD8gNd768698cp1FDuSmFQZHVZGk+Og==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz", + "integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==", "dev": true, "requires": { "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", - "@types/sinonjs__fake-timers": "^6.0.2", + "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", - "bluebird": "3.7.2", + "bluebird": "^3.7.2", + "buffer": "^5.6.0", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -13755,6 +13793,16 @@ "yauzl": "^2.10.0" }, "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -13808,6 +13856,13 @@ } } }, + "cypress-file-upload": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz", + "integrity": "sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==", + "dev": true, + "requires": {} + }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 1d1a8d840..827e6a946 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,7 @@ }, "devDependencies": { "autoprefixer": "^10.4.1", - "cypress": "^9.2.1", + "cypress": "^9.3.1", "cypress-file-upload": "^5.0.8", "gettext-parser": "^4.2.0", "gulp": "4.0.2", diff --git a/frontend/src/app/main/ui/auth/register.cljs b/frontend/src/app/main/ui/auth/register.cljs index 9fabc8d5c..6516c43a4 100644 --- a/frontend/src/app/main/ui/auth/register.cljs +++ b/frontend/src/app/main/ui/auth/register.cljs @@ -237,7 +237,7 @@ (mf/defc register-validate-page [{:keys [params] :as props}] [:div.form-container - [:h1 (tr "auth.register-title")] + [:h1 {:data-test "register-title"} (tr "auth.register-title")] [:div.subtitle (tr "auth.register-subtitle")] [:& register-validate-form {:params params}] diff --git a/frontend/src/app/main/ui/components/context_menu.cljs b/frontend/src/app/main/ui/components/context_menu.cljs index b83efcc14..908e992e4 100644 --- a/frontend/src/app/main/ui/components/context_menu.cljs +++ b/frontend/src/app/main/ui/components/context_menu.cljs @@ -101,7 +101,7 @@ [:span i/arrow-slide] parent-option]] [:li.separator]]) - (for [[index [option-name option-handler sub-options]] (d/enumerate (:options level))] + (for [[index [option-name option-handler sub-options data-test]] (d/enumerate (:options level))] (when option-name (if (= option-name :separator) [:li.separator] @@ -111,12 +111,14 @@ (if-not sub-options [:a.context-menu-action {:on-click #(do (dom/stop-propagation %) (on-close) - (option-handler %))} + (option-handler %)) + :data-test data-test} (if (and in-dashboard? (= option-name "Default")) (tr "dashboard.default-team-name") option-name)] [:a.context-menu-action.submenu {:data-no-close true - :on-click (enter-submenu option-name sub-options)} + :on-click (enter-submenu option-name sub-options) + :data-test data-test} option-name [:span i/arrow-slide]])])))])]]))) diff --git a/frontend/src/app/main/ui/dashboard/comments.cljs b/frontend/src/app/main/ui/dashboard/comments.cljs index 4fd92a474..582ea47ac 100644 --- a/frontend/src/app/main/ui/dashboard/comments.cljs +++ b/frontend/src/app/main/ui/dashboard/comments.cljs @@ -55,6 +55,7 @@ [:div.dashboard-comments-section [:div.button {:on-click show-dropdown + :data-test "open-comments" :class (dom/classnames :open @show-dropdown? :unread (boolean (seq tgroups)))} i/chat] diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index dba16d15b..085f26b9b 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -203,27 +203,28 @@ (for [sub-project (:projects team)] [(get-project-name sub-project) (on-move (:id team) - (:id sub-project))])])])) + (:id sub-project))])]) + "move-to-other-team"])) options (if multi? - [[(tr "dashboard.duplicate-multi" file-count) on-duplicate] + [[(tr "dashboard.duplicate-multi" file-count) on-duplicate nil "duplicate-multi"] (when (or (seq current-projects) (seq other-teams)) - [(tr "dashboard.move-to-multi" file-count) nil sub-options]) + [(tr "dashboard.move-to-multi" file-count) nil sub-options "move-to-multi"]) [(tr "dashboard.export-multi" file-count) on-export-files] [:separator] - [(tr "labels.delete-multi-files" file-count) on-delete]] + [(tr "labels.delete-multi-files" file-count) on-delete nil "delete-multi-files"]] [[(tr "dashboard.open-in-new-tab") on-new-tab] - [(tr "labels.rename") on-edit] - [(tr "dashboard.duplicate") on-duplicate] + [(tr "labels.rename") on-edit nil "file-rename"] + [(tr "dashboard.duplicate") on-duplicate nil "file-duplicate"] (when (or (seq current-projects) (seq other-teams)) - [(tr "dashboard.move-to") nil sub-options]) + [(tr "dashboard.move-to") nil sub-options "file-move-to"]) (if (:is-shared file) - [(tr "dashboard.remove-shared") on-del-shared] - [(tr "dashboard.add-shared") on-add-shared]) - [(tr "dashboard.export-single") on-export-files] + [(tr "dashboard.remove-shared") on-del-shared nil "file-del-shared"] + [(tr "dashboard.add-shared") on-add-shared nil "file-add-shared"]) + [(tr "dashboard.export-single") on-export-files nil "file-export"] [:separator] - [(tr "labels.delete") on-delete]])] + [(tr "labels.delete") on-delete nil "file-delete"]])] [:& context-menu {:on-close on-menu-close :show show? diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index 9d050206a..770b67f54 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -70,7 +70,7 @@ (with-meta {::ev/origin "project"})))) (swap! local assoc :edition false)))}] [:div.dashboard-title - [:h1 {:on-double-click on-edit} + [:h1 {:on-double-click on-edit :data-test "project-title"} (:name project)]])) [:& project-menu {:project project @@ -82,7 +82,7 @@ :on-import on-import}] [:div.dashboard-header-actions - [:a.btn-secondary.btn-small {:on-click on-create-clicked} + [:a.btn-secondary.btn-small {:on-click on-create-clicked :data-test "new-file"} (tr "dashboard.new-file")] (when-not (:is-default project) diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs index 0199f4213..706bc1fb6 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.cljs +++ b/frontend/src/app/main/ui/dashboard/fonts.cljs @@ -149,10 +149,10 @@ [:span (tr "dashboard.fonts.fonts-added" (i18n/c (count (vals @fonts))))] [:div.table-field.options [:div.btn-primary - {:on-click #(on-upload-all (vals @fonts))} + {:on-click #(on-upload-all (vals @fonts)) :data-test "upload-all"} [:span (tr "dashboard.fonts.upload-all")]] [:div.btn-secondary - {:on-click #(on-dismiss-all (vals @fonts))} + {:on-click #(on-dismiss-all (vals @fonts)) :data-test "dismiss-all"} [:span (tr "dashboard.fonts.dismiss-all")]]]]) (for [item (sort-by :font-family (vals @fonts))] @@ -277,8 +277,8 @@ :fixed? false :top -15 :left -115 - :options [[(tr "labels.edit") #(reset! edit? true)] - [(tr "labels.delete") on-delete]]}]])])) + :options [[(tr "labels.edit") #(reset! edit? true) nil "font-edit"] + [(tr "labels.delete") on-delete nil "font-delete"]]}]])])) (mf/defc installed-fonts diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index 6edfbb955..1f80a452d 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -21,7 +21,7 @@ [potok.core :as ptk] [rumext.alpha :as mf])) -(log/set-level! :warn) +(log/set-level! :debug) (def ^:const emit-delay 1000) diff --git a/frontend/src/app/main/ui/dashboard/project_menu.cljs b/frontend/src/app/main/ui/dashboard/project_menu.cljs index 1df238537..c374ef16d 100644 --- a/frontend/src/app/main/ui/dashboard/project_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/project_menu.cljs @@ -108,19 +108,20 @@ :top top :left left :options [(when-not (:is-default project) - [(tr "labels.rename") on-edit]) + [(tr "labels.rename") on-edit nil "project-rename"]) (when-not (:is-default project) - [(tr "dashboard.duplicate") on-duplicate]) + [(tr "dashboard.duplicate") on-duplicate nil "project-duplicate"]) (when-not (:is-default project) [(tr "dashboard.pin-unpin") toggle-pin]) (when (and (seq teams) (not (:is-default project))) [(tr "dashboard.move-to") nil (for [team teams] - [(:name team) (on-move (:id team))])]) + [(:name team) (on-move (:id team))]) + "project-move-to"]) (when (some? on-import) - [(tr "dashboard.import") on-import-files]) + [(tr "dashboard.import") on-import-files nil "file-import"]) (when-not (:is-default project) [:separator]) (when-not (:is-default project) - [(tr "labels.delete") on-delete])]}]])) + [(tr "labels.delete") on-delete nil "project-delete"])]}]])) diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 9a3ddc966..b8352a11f 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -30,7 +30,7 @@ [:div.dashboard-title [:h1 (tr "dashboard.projects-title")]] - [:a.btn-secondary.btn-small {:on-click create} + [:a.btn-secondary.btn-small {:on-click create :data-test "new-project-button"} (tr "dashboard.new-project")]])) (mf/defc project-item @@ -140,11 +140,11 @@ i/pin)]) [:a.btn-secondary.btn-small.tooltip.tooltip-bottom - {:on-click create-file :alt (tr "dashboard.new-file")} + {:on-click create-file :alt (tr "dashboard.new-file") :data-test "project-new-file"} i/close] [:a.btn-secondary.btn-small.tooltip.tooltip-bottom - {:on-click on-menu-click :alt (tr "dashboard.options")} + {:on-click on-menu-click :alt (tr "dashboard.options") :data-test "project-options"} i/actions]] [:& line-grid diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index f632ca2f4..5f0c3f715 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -221,7 +221,7 @@ [:span.team-text {:title (:name team)} (:name team)]]]) [:hr] - [:li.action {:on-click on-create-clicked} + [:li.action {:on-click on-create-clicked :data-test "create-new-team"} (tr "dashboard.create-new-team")]])) (s/def ::member-id ::us/uuid) @@ -349,21 +349,21 @@ :on-accept delete-fn}))] [:ul.dropdown.options-dropdown - [:li {:on-click go-members} (tr "labels.members")] - [:li {:on-click go-settings} (tr "labels.settings")] + [:li {:on-click go-members :data-test "team-members"} (tr "labels.members")] + [:li {:on-click go-settings :data-test "team-settings"} (tr "labels.settings")] [:hr] - [:li {:on-click on-rename-clicked} (tr "labels.rename")] + [:li {:on-click on-rename-clicked :data-test "rename-team"} (tr "labels.rename")] (cond (get-in team [:permissions :is-owner]) - [:li {:on-click on-leave-as-owner-clicked} (tr "dashboard.leave-team")] + [:li {:on-click on-leave-as-owner-clicked :data-test "leave-team"} (tr "dashboard.leave-team")] (> (count members) 1) [:li {:on-click on-leave-clicked} (tr "dashboard.leave-team")]) (when (get-in team [:permissions :is-owner]) - [:li {:on-click on-delete-clicked} (tr "dashboard.delete-team")])])) + [:li {:on-click on-delete-clicked :data-test "delete-team"} (tr "dashboard.delete-team")])])) (mf/defc sidebar-team-switch @@ -466,13 +466,14 @@ [:div.sidebar-content-section [:ul.sidebar-nav.no-overflow - [:li.recent-projects + [:li {:on-click go-fonts + :data-test "fonts" :class-name (when fonts? "current")} [:span.element-title (tr "labels.fonts")]]]] [:hr] - [:div.sidebar-content-section + [:div.sidebar-content-section {:data-test "pinned-projects"} (if (seq pinned-projects) [:ul.sidebar-nav (for [item pinned-projects] diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index 35758737d..6d98ddae7 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -48,7 +48,7 @@ [:a {:on-click go-settings} (tr "labels.settings")]]]] (if (and members-section? (:is-admin permissions)) - [:a.btn-secondary.btn-small {:on-click invite-member} + [:a.btn-secondary.btn-small {:on-click invite-member :data-test "invite-member"} (tr "dashboard.invite-profile")] [:div])])) diff --git a/frontend/src/app/main/ui/onboarding/team_choice.cljs b/frontend/src/app/main/ui/onboarding/team_choice.cljs index abaaa8d93..56f3c5b2d 100644 --- a/frontend/src/app/main/ui/onboarding/team_choice.cljs +++ b/frontend/src/app/main/ui/onboarding/team_choice.cljs @@ -51,7 +51,7 @@ [:h2 (tr "onboarding.choice.fly-solo")] [:p (tr "onboarding.choice.fly-solo-desc")]]] [:div.modal-right - [:div.content-button {:on-click on-team-up} + [:div.content-button {:on-click on-team-up :data-test "team-up-button"} [:h2 (tr "onboarding.choice.team-up")] [:p (tr "onboarding.choice.team-up-desc")]]]] [:img.deco {:src "images/deco-left.png" :border "0"}] @@ -72,7 +72,7 @@ [:div.modal-overlay [:div.modal-container.onboarding-team [:div.title - [:h2 (tr "onboarding.choice.team-up")] + [:h2 {:data-test "onboarding-choice-team-up"} (tr "onboarding.choice.team-up")] [:p (tr "onboarding.choice.team-up-desc")]] [:& fm/form {:form form diff --git a/frontend/src/app/main/ui/onboarding/templates.cljs b/frontend/src/app/main/ui/onboarding/templates.cljs index 84bfc1558..c8ef3e327 100644 --- a/frontend/src/app/main/ui/onboarding/templates.cljs +++ b/frontend/src/app/main/ui/onboarding/templates.cljs @@ -74,7 +74,7 @@ :data-test "close-templates-btn"} i/close]] [:div.modal-content - [:h3 (tr "onboarding.templates.title")] + [:h3 {:data-test "onboarding-templates-title"} (tr "onboarding.templates.title")] [:p (tr "onboarding.templates.subtitle")] [:div.templates diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index 102a66cb4..0b9cce30d 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -87,7 +87,7 @@ [:hr] - [:li {:on-click show-release-notes} + [:li {:on-click show-release-notes :data-test "release-notes"} i/pencil [:span.element-title (tr "labels.release-notes")]] diff --git a/frontend/src/app/main/ui/workspace/left_toolbar.cljs b/frontend/src/app/main/ui/workspace/left_toolbar.cljs index 94c48e702..be41b8e6d 100644 --- a/frontend/src/app/main/ui/workspace/left_toolbar.cljs +++ b/frontend/src/app/main/ui/workspace/left_toolbar.cljs @@ -75,17 +75,20 @@ [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame)) :class (when (= selected-drawtool :frame) "selected") - :on-click (partial select-drawtool :frame)} + :on-click (partial select-drawtool :frame) + :data-test "artboard-btn"} i/artboard] [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect)) :class (when (= selected-drawtool :rect) "selected") - :on-click (partial select-drawtool :rect)} + :on-click (partial select-drawtool :rect) + :data-test "rect-btn"} i/box] [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse)) :class (when (= selected-drawtool :circle) "selected") - :on-click (partial select-drawtool :circle)} + :on-click (partial select-drawtool :circle) + :data-test "ellipse-btn"} i/circle] [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text)) @@ -98,12 +101,14 @@ [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve)) :class (when (= selected-drawtool :curve) "selected") - :on-click (partial select-drawtool :curve)} + :on-click (partial select-drawtool :curve) + :data-test "curve-btn"} i/pencil] [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path)) :class (when (= selected-drawtool :path) "selected") - :on-click (partial select-drawtool :path)} + :on-click (partial select-drawtool :path) + :data-test "path-btn"} i/pen] [:li.tooltip.tooltip-right diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4f4095cce..9ad2d6ad5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -167,10 +167,10 @@ resolved "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz" integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== -"@types/sinonjs__fake-timers@^6.0.2": - version "6.0.4" - resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz" - integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A== +"@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== "@types/sizzle@^2.3.2": version "2.3.3" @@ -538,7 +538,7 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.0.2: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -590,7 +590,7 @@ blob-util@^2.0.2: resolved "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz" integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== -bluebird@3.7.2: +bluebird@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -766,6 +766,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz" @@ -1406,22 +1414,23 @@ csstype@^3.0.2: cypress-file-upload@^5.0.8: version "5.0.8" - resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1" + resolved "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz" integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g== -cypress@^9.2.1: - version "9.2.1" - resolved "https://registry.npmjs.org/cypress/-/cypress-9.2.1.tgz" - integrity sha512-LVEe4yWCo4xO0Vd8iYjFHRyd5ulRvM56XqMgAdn05Qb9kJ6iJdO/MmjKD8gNd768698cp1FDuSmFQZHVZGk+Og== +cypress@^9.3.1: + version "9.3.1" + resolved "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz" + integrity sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" "@types/node" "^14.14.31" - "@types/sinonjs__fake-timers" "^6.0.2" + "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" blob-util "^2.0.2" - bluebird "3.7.2" + bluebird "^3.7.2" + buffer "^5.6.0" cachedir "^2.3.0" chalk "^4.1.0" check-more-types "^2.24.0" @@ -2808,7 +2817,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.4: +ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==