diff --git a/app/client/.dockerignore b/app/client/.dockerignore new file mode 100644 index 0000000000..4376f41539 --- /dev/null +++ b/app/client/.dockerignore @@ -0,0 +1,5 @@ +.git +.idea +node_modules +build +build.tgz \ No newline at end of file diff --git a/app/client/.gitignore b/app/client/.gitignore index ec9dcd5bd9..33be47b5f7 100755 --- a/app/client/.gitignore +++ b/app/client/.gitignore @@ -28,3 +28,5 @@ yarn-error.log* /src/assets/icons/fonts/* .idea .storybook-out/ +cypress/videos +results/ \ No newline at end of file diff --git a/app/client/.gitlab-ci.yml b/app/client/.gitlab-ci.yml new file mode 100644 index 0000000000..7d38185c39 --- /dev/null +++ b/app/client/.gitlab-ci.yml @@ -0,0 +1,83 @@ +.only-default: &only-default + only: + - release + - master + - merge_requests + +image: cypress/base:10.16.3 + +variables: + npm_config_cache: "$CI_PROJECT_DIR/.npm" + CYPRESS_CACHE_FOLDER: "$CI_PROJECT_DIR/cache/Cypress" + DOCKER_DRIVER: overlay + DOCKER_IMAGE_NAME: $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$CI_COMMIT_SHORT_SHA + +cache: + key: ${CI_COMMIT_REF_SLUG} + paths: + - .npm + - cache/Cypress + - node_modules + +stages: + - build + - test + - package + - deploy + +react-build: + stage: build + script: + - yarn install + # show where the Cypress test runner binaries are cached + - $(npm bin)/cypress cache path + # show all installed versions of Cypress binary + - $(npm bin)/cypress cache list + - $(npm bin)/cypress verify + only: + - release + - merge_requests + +cypress-test: + stage: test + script: + - REACT_APP_ENVIRONMENT=DEVELOPMENT REACT_APP_BASE_URL="https://release-api.appsmith.com" GIT_SHA=$CI_COMMIT_SHORT_SHA yarn build + - yarn global add serve + - serve -s build -p 3000 & + # This is required in order to ensure that all the test cases pass + - echo "127.0.0.1 dev.appsmith.com" >> /etc/hosts + - yarn test + artifacts: + expire_in: 1 week + paths: + - build/ + - cypress/screenshots + - cypress/videos + only: + # We don't test on master right now because of changing environment variables for REACT_APP_BASE_URL. Need to figure out a way to configure that. + - release + - merge_requests + +docker-package-release: + image: docker:dind + services: + - docker:dind + stage: package + script: + - docker build --build-arg REACT_APP_ENVIRONMENT=STAGING --build-arg GIT_SHA=$CI_COMMIT_SHORT_SHA -t appsmith/appsmith-editor:release . + - docker login -u $DOCKER_HUB_USERNAME -p $DOCKER_HUB_ACCESS_TOKEN + - docker push appsmith/appsmith-editor:release + only: + - release + +docker-package-prod: + image: docker:dind + services: + - docker:dind + stage: package + script: + - docker build --build-arg REACT_APP_ENVIRONMENT=PRODUCTION --build-arg GIT_SHA=$CI_COMMIT_SHORT_SHA -t appsmith/appsmith-editor:latest . + - docker login -u $DOCKER_HUB_USERNAME -p $DOCKER_HUB_ACCESS_TOKEN + - docker push appsmith/appsmith-editor:latest + only: + - master diff --git a/app/client/Dockerfile b/app/client/Dockerfile new file mode 100644 index 0000000000..92c0e97200 --- /dev/null +++ b/app/client/Dockerfile @@ -0,0 +1,22 @@ +FROM node:10.19-alpine as build-deps + +WORKDIR /usr/src/app + +ARG REACT_APP_ENVIRONMENT="DEVELOPMENT" +ARG GIT_SHA="" + +ENV REACT_APP_ENVIRONMENT=${REACT_APP_ENVIRONMENT} +ENV REACT_APP_BASE_URL=${REACT_APP_BASE_URL} +ENV GIT_SHA=${GIT_SHA} + +COPY package.json yarn.lock ./ +COPY . ./ +RUN yarn install && yarn build + +# Use the output from the previous Docker build to create the nginx container +FROM nginx:1.17.9-alpine as final-image +COPY --from=build-deps /usr/src/app/docker/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build-deps /usr/src/app/build /var/www/appsmith + +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/app/client/build.sh b/app/client/build.sh index f924248d5f..c71f8d9d7a 100755 --- a/app/client/build.sh +++ b/app/client/build.sh @@ -1,7 +1,6 @@ - #!/bin/sh -GIT_SHA=$(eval git rev-parse HEAD) -GIT_BRANCH=$(git branch --no-color | grep -E '^\*' | sed 's/\*[^a-z]*//g') +# GIT_SHA=$(eval git rev-parse HEAD) +# GIT_BRANCH=$(git branch --no-color | grep -E '^\*' | sed 's/\*[^a-z]*//g') # RELEASE="${GIT_BRANCH}_${GIT_SHA}" @@ -9,7 +8,7 @@ GIT_BRANCH=$(git branch --no-color | grep -E '^\*' | sed 's/\*[^a-z]*//g') # RELEASE=$(echo "$RELEASE" | sed -e 's/[\/\\\ .]/\-/g') # echo $RELEASE -REACT_APP_SENTRY_RELEASE=$GIT_SHA craco --max-old-space-size=2048 build --config craco.build.config.js +REACT_APP_SENTRY_RELEASE=$GIT_SHA craco --max-old-space-size=4096 build --config craco.build.config.js -rm $PWD/build/static/js/*.js.map +rm ./build/static/js/*.js.map echo "build finished" \ No newline at end of file diff --git a/app/client/cypress.json b/app/client/cypress.json index 0967ef424b..3c75281b43 100644 --- a/app/client/cypress.json +++ b/app/client/cypress.json @@ -1 +1,11 @@ -{} +{ + "baseUrl":"http://dev.appsmith.com:3000/", + + "reporter": "mochawesome", + "reporterOptions": { + "reportDir": "results", + "overwrite": false, + "html": false, + "json": true + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/user.json b/app/client/cypress/fixtures/user.json new file mode 100644 index 0000000000..9d5a67368d --- /dev/null +++ b/app/client/cypress/fixtures/user.json @@ -0,0 +1,6 @@ + + { + "username": "testowner@appsmith.com", + "password": "own3rT3st1ng", + + } \ No newline at end of file diff --git a/app/client/cypress/integration/OnBoarding/Login_spec.js b/app/client/cypress/integration/OnBoarding/Login_spec.js new file mode 100644 index 0000000000..ba0cb7ea2c --- /dev/null +++ b/app/client/cypress/integration/OnBoarding/Login_spec.js @@ -0,0 +1,20 @@ +var loginPage= require('../../locators/LoginPage.json') +const loginData=require('../../fixtures/user.json') + +context('Cypress test',function() { + +it('Login functionality',function(){ + + cy.LogintoApp(loginData.username,loginData.password) + cy.get('input[type="text"]').type('Test app') + cy.wait(3000) + cy.get('.t--application-edit-link').click() + cy.wait(5000) + cy.get('.t--draggable-buttonwidget').click({ force: true }) + cy.wait(2000) + cy.get('textarea').first().click({ force: true }).clear({ force: true }).type('Test', { force: true }) + cy.wait(5000) + cy.get('.t--application-publish-btn').click() + +}) +}) \ No newline at end of file diff --git a/app/client/cypress/integration/examples/actions.spec.js b/app/client/cypress/integration/examples/actions.spec.js deleted file mode 100644 index 03eba5e333..0000000000 --- a/app/client/cypress/integration/examples/actions.spec.js +++ /dev/null @@ -1,309 +0,0 @@ -/// - -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