diff --git a/scm-ui/e2e-tests/cypress.json b/scm-ui/e2e-tests/cypress.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/scm-ui/e2e-tests/cypress.json @@ -0,0 +1 @@ +{} diff --git a/scm-ui/e2e-tests/cypress/fixtures/example.json b/scm-ui/e2e-tests/cypress/fixtures/example.json new file mode 100644 index 0000000000..da18d9352a --- /dev/null +++ b/scm-ui/e2e-tests/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/scm-ui/e2e-tests/cypress/integration/anonymousAccess.spec.js b/scm-ui/e2e-tests/cypress/integration/anonymousAccess.spec.js new file mode 100644 index 0000000000..5c6d76568c --- /dev/null +++ b/scm-ui/e2e-tests/cypress/integration/anonymousAccess.spec.js @@ -0,0 +1,172 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +describe("With Anonymous mode disabled", () => { + it("Should show login page without primary navigation", () => { + setUserPermissions("_anonymous", ["repository:read,pull:*"]); + setAnonymousMode("OFF"); + + cy.visit("http://localhost:8081/scm/repos/"); + cy.contains("Please login to proceed"); + cy.get("div").not("Login"); + cy.get("div").not("Repositories"); + }); + it("Should redirect after login", () => { + cy.visit("http://localhost:8081/scm/me/"); + cy.get("div.field.username > div > input").type("scmadmin"); + cy.get("div.field.password > div > input").type("scmadmin"); + cy.get("button") + .contains("Login") + .click(); + cy.contains("Profile"); + }); +}); + +describe("With Anonymous mode protocol only enabled", () => { + it("Should show login page without primary navigation", () => { + setUserPermissions("_anonymous", ["repository:read,pull:*"]); + setAnonymousMode("PROTOCOL_ONLY"); + + cy.visit("http://localhost:8081/scm/repos/"); + cy.contains("Please login to proceed"); + cy.get("div").not("Login"); + cy.get("div").not("Repositories"); + }); +}); + +describe("With Anonymous mode fully enabled", () => { + it("Should show repositories overview with Login button in primary navigation", () => { + setUserPermissions("_anonymous", ["repository:read,pull:*"]); + setAnonymousMode("FULL"); + + cy.visit("http://localhost:8081/scm/repos/"); + cy.contains("Overview of available repositories"); + cy.contains("SCM Anonymous"); + cy.get("ul").contains("Login"); + }); + it("Should show login page on url", () => { + cy.visit("http://localhost:8081/scm/login/"); + }); + it("Should show login page on link click", () => { + cy.visit("http://localhost:8081/scm/repos/"); + cy.contains("Overview of available repositories"); + cy.contains("Login").click(); + cy.contains("Please login to proceed"); + }); + it("Should login and direct to repositories overview", () => { + loginUser("scmadmin", "scmadmin"); + + cy.visit("http://localhost:8081/scm/login"); + cy.contains("SCM Administrator"); + }); + it("Should logout and direct to login page", () => { + loginUser("scmadmin", "scmadmin"); + + cy.visit("http://localhost:8081/scm/repos/"); + cy.contains("Overview of available repositories"); + cy.contains("SCM Administrator"); + cy.contains("Logout").click(); + cy.contains("Please login to proceed"); + }); + it("Anonymous user should not be able to change password", () => { + cy.visit("http://localhost:8081/scm/repos/"); + cy.contains("Profile").click(); + cy.contains("scm-anonymous@scm-manager.org"); + cy.get("ul").not("Settings"); + cy.get("section").not("Change password"); + }); +}); + +const loginUser = (username, password) => { + const loginUrl = `http://localhost:8081/scm/api/v2/auth/access_token`; + + cy.request({ + method: "POST", + url: loginUrl, + body: { + cookie: true, + username: username, + password: password, + grantType: "password" + } + }); +}; + +const setUserPermissions = (user, permissions) => { + const MEDIA_TYPE = "application/vnd.scmm-permissionCollection+json;v=2"; + const userPermissionUrl = `http://localhost:8081/scm/api/v2/users/${user}/permissions`; + + cy.request({ + method: "PUT", + url: userPermissionUrl, + body: { permissions: permissions }, + headers: { "content-type": MEDIA_TYPE }, + auth: { + user: "scmadmin", + pass: "scmadmin", + sendImmediately: true + } + }); +}; + +const setAnonymousMode = anonMode => { + const MEDIA_TYPE = "application/vnd.scmm-config+json;v=2"; + + const content = { + adminGroups: [], + adminUsers: [], + anonymousMode: anonMode, + baseUrl: "http://localhost:8081/scm", + dateFormat: "YYYY-MM-DD HH:mm:ss", + disableGroupingGrid: false, + enableProxy: false, + enabledXsrfProtection: false, + forceBaseUrl: false, + loginAttemptLimit: 100, + loginAttemptLimitTimeout: 300, + loginInfoUrl: "https://login-info.scm-manager.org/api/v1/login-info", + namespaceStrategy: "UsernameNamespaceStrategy", + pluginUrl: + "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/plugin-snapshot/job/master/lastSuccessfulBuild/artifact/plugins/plugin-center.json", + proxyExcludes: [], + proxyPassword: null, + proxyPort: 8080, + proxyServer: "proxy.mydomain.com", + proxyUser: null, + realmDescription: "SONIA :: SCM Manager", + skipFailedAuthenticators: false + }; + + cy.request({ + method: "PUT", + url: "http://localhost:8081/scm/api/v2/config", + body: content, + headers: { "content-type": MEDIA_TYPE }, + auth: { + user: "scmadmin", + pass: "scmadmin", + sendImmediately: true + } + }); +}; diff --git a/scm-ui/e2e-tests/cypress/integration/examples/actions.spec.js b/scm-ui/e2e-tests/cypress/integration/examples/actions.spec.js new file mode 100644 index 0000000000..23bd257715 --- /dev/null +++ b/scm-ui/e2e-tests/cypress/integration/examples/actions.spec.js @@ -0,0 +1,322 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/// + +context('Actions', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/actions') + }) + + // https://on.cypress.io/interacting-with-elements + + it('.type() - type into a DOM element', () => { + // https://on.cypress.io/type + cy.get('.action-email') + .type('fake@email.com').should('have.value', 'fake@email.com') + + // .type() with special character sequences + .type('{leftarrow}{rightarrow}{uparrow}{downarrow}') + .type('{del}{selectall}{backspace}') + + // .type() with key modifiers + .type('{alt}{option}') //these are equivalent + .type('{ctrl}{control}') //these are equivalent + .type('{meta}{command}{cmd}') //these are equivalent + .type('{shift}') + + // Delay each keypress by 0.1 sec + .type('slow.typing@email.com', { delay: 100 }) + .should('have.value', 'slow.typing@email.com') + + cy.get('.action-disabled') + // Ignore error checking prior to type + // like whether the input is visible or disabled + .type('disabled error checking', { force: true }) + .should('have.value', 'disabled error checking') + }) + + it('.focus() - focus on a DOM element', () => { + // https://on.cypress.io/focus + cy.get('.action-focus').focus() + .should('have.class', 'focus') + .prev().should('have.attr', 'style', 'color: orange;') + }) + + it('.blur() - blur off a DOM element', () => { + // https://on.cypress.io/blur + cy.get('.action-blur').type('About to blur').blur() + .should('have.class', 'error') + .prev().should('have.attr', 'style', 'color: red;') + }) + + it('.clear() - clears an input or textarea element', () => { + // https://on.cypress.io/clear + cy.get('.action-clear').type('Clear this text') + .should('have.value', 'Clear this text') + .clear() + .should('have.value', '') + }) + + it('.submit() - submit a form', () => { + // https://on.cypress.io/submit + cy.get('.action-form') + .find('[type="text"]').type('HALFOFF') + + cy.get('.action-form').submit() + .next().should('contain', 'Your form has been submitted!') + }) + + it('.click() - click on a DOM element', () => { + // https://on.cypress.io/click + cy.get('.action-btn').click() + + // You can click on 9 specific positions of an element: + // ----------------------------------- + // | topLeft top topRight | + // | | + // | | + // | | + // | left center right | + // | | + // | | + // | | + // | bottomLeft bottom bottomRight | + // ----------------------------------- + + // clicking in the center of the element is the default + cy.get('#action-canvas').click() + + cy.get('#action-canvas').click('topLeft') + cy.get('#action-canvas').click('top') + cy.get('#action-canvas').click('topRight') + cy.get('#action-canvas').click('left') + cy.get('#action-canvas').click('right') + cy.get('#action-canvas').click('bottomLeft') + cy.get('#action-canvas').click('bottom') + cy.get('#action-canvas').click('bottomRight') + + // .click() accepts an x and y coordinate + // that controls where the click occurs :) + + cy.get('#action-canvas') + .click(80, 75) // click 80px on x coord and 75px on y coord + .click(170, 75) + .click(80, 165) + .click(100, 185) + .click(125, 190) + .click(150, 185) + .click(170, 165) + + // click multiple elements by passing multiple: true + cy.get('.action-labels>.label').click({ multiple: true }) + + // Ignore error checking prior to clicking + cy.get('.action-opacity>.btn').click({ force: true }) + }) + + it('.dblclick() - double click on a DOM element', () => { + // https://on.cypress.io/dblclick + + // Our app has a listener on 'dblclick' event in our 'scripts.js' + // that hides the div and shows an input on double click + cy.get('.action-div').dblclick().should('not.be.visible') + cy.get('.action-input-hidden').should('be.visible') + }) + + it('.rightclick() - right click on a DOM element', () => { + // https://on.cypress.io/rightclick + + // Our app has a listener on 'contextmenu' event in our 'scripts.js' + // that hides the div and shows an input on right click + cy.get('.rightclick-action-div').rightclick().should('not.be.visible') + cy.get('.rightclick-action-input-hidden').should('be.visible') + }) + + it('.check() - check a checkbox or radio element', () => { + // https://on.cypress.io/check + + // By default, .check() will check all + // matching checkbox or radio elements in succession, one after another + cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]') + .check().should('be.checked') + + cy.get('.action-radios [type="radio"]').not('[disabled]') + .check().should('be.checked') + + // .check() accepts a value argument + cy.get('.action-radios [type="radio"]') + .check('radio1').should('be.checked') + + // .check() accepts an array of values + cy.get('.action-multiple-checkboxes [type="checkbox"]') + .check(['checkbox1', 'checkbox2']).should('be.checked') + + // Ignore error checking prior to checking + cy.get('.action-checkboxes [disabled]') + .check({ force: true }).should('be.checked') + + cy.get('.action-radios [type="radio"]') + .check('radio3', { force: true }).should('be.checked') + }) + + it('.uncheck() - uncheck a checkbox element', () => { + // https://on.cypress.io/uncheck + + // By default, .uncheck() will uncheck all matching + // checkbox elements in succession, one after another + cy.get('.action-check [type="checkbox"]') + .not('[disabled]') + .uncheck().should('not.be.checked') + + // .uncheck() accepts a value argument + cy.get('.action-check [type="checkbox"]') + .check('checkbox1') + .uncheck('checkbox1').should('not.be.checked') + + // .uncheck() accepts an array of values + cy.get('.action-check [type="checkbox"]') + .check(['checkbox1', 'checkbox3']) + .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked') + + // Ignore error checking prior to unchecking + cy.get('.action-check [disabled]') + .uncheck({ force: true }).should('not.be.checked') + }) + + it('.select() - select an option in a