diff --git a/.github/workflows/integration-tests-against-emulator.yaml b/.github/workflows/integration-tests-against-emulator.yaml index 8a96013f0..07c17b664 100644 --- a/.github/workflows/integration-tests-against-emulator.yaml +++ b/.github/workflows/integration-tests-against-emulator.yaml @@ -149,6 +149,29 @@ jobs: - run: gcloud spanner instances create test-instance --config=emulator-config --description="Test Instance" --nodes=1 # run tests + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: 16 + + - name: Install Dependencies + run: | + cd ui + npm ci + + - name: Start Local Server + run: | + cd ui + npm start & + + - name: Wait for Local Server to Start + run: npx wait-on http://localhost:4200 -t 30000 + + - name: Cypress run + run: | + cd ui + npx cypress run + - uses: actions/setup-go@v2 with: go-version: "1.19" @@ -157,4 +180,4 @@ jobs: env: SPANNER_EMULATOR_HOST: localhost:9010 SPANNER_MIGRATION_TOOL_TESTS_GCLOUD_PROJECT_ID: emulator-test-project - SPANNER_MIGRATION_TOOL_TESTS_GCLOUD_INSTANCE_ID: test-instance + SPANNER_MIGRATION_TOOL_TESTS_GCLOUD_INSTANCE_ID: test-instance \ No newline at end of file diff --git a/ui/cypress.config.ts b/ui/cypress.config.ts new file mode 100644 index 000000000..cb0b78113 --- /dev/null +++ b/ui/cypress.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "cypress"; + +export default defineConfig({ + e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, + supportFile: false + }, +}); diff --git a/ui/cypress/e2e/spec.cy.ts b/ui/cypress/e2e/spec.cy.ts new file mode 100644 index 000000000..71e948140 --- /dev/null +++ b/ui/cypress/e2e/spec.cy.ts @@ -0,0 +1,88 @@ +import mockIConv from "../../src/mocks/conv"; + +describe('template spec', () => { + let url = window.location.origin; + beforeEach(() => { + // Intercept the backend APIs and return desired response. + cy.intercept('GET', `${url}/IsOffline`, { statusCode: 200, body: false }).as('getIsOffline'); + cy.intercept('GET', `${url}/GetSessions`, { statusCode: 200, body: [] }).as('getSessions'); + cy.intercept('GET', `${url}/GetConfig`, { statusCode: 200 }).as('getConfig'); + cy.intercept('GET', `${url}/GetLatestSessionDetails`, { statusCode: 200 }).as('getLatestSessionDetails'); + cy.visit('http://localhost:4200/'); + }); + + it('verify direct connection to mysql non-sharded database', () => { + cy.intercept('GET', `${url}/ping`, { statusCode: 200 }).as('checkBackendHealth'); + cy.intercept('GET', `${url}/convert/infoschema`, { statusCode: 200, body: mockIConv }).as('directConnection'); + + cy.get('.primary-header').eq(0).should('have.text', 'Get started with Spanner migration tool'); + cy.get('#edit-icon').should('exist').click(); + + cy.fixture('config').then((json) => { + cy.intercept('POST', `${url}/SetSpannerConfig`, (req) => { + req.reply({ + status: 200, + body: { + GCPProjectID: json.projectId, + SpannerInstanceID: json.instanceId, + IsMetadataDbCreated: false, + IsConfigValid: true, + }, + }); + }).as('setSpannerConfig'); + cy.get('#project-id').clear().type(json.projectId) + cy.get('#instance-id').clear().type(json.instanceId) + }) + + cy.get('#save-button').click(); + cy.get('#save-button', { timeout: 50000 }).should("not.exist"); + cy.get('#check-icon').should('exist'); + cy.get('#connect-to-database-btn', { timeout: 10000 }).should('exist'); + cy.get('#connect-to-database-btn').click(); + + // Wait for the connection to complete + cy.get('#direct-connection-component', { timeout: 10000 }).should('be.visible'); + + cy.get('#dbengine-input').click(); + cy.get('mat-option').contains('MySQL').click(); + + cy.fixture('mysql-config').then((json) => { + cy.intercept('POST', `${url}/connect`, (req) => { + if (req.body && req.body.Host === json.hostname && req.body.User === json.username && req.body.Driver === 'mysql' + && req.body.Password === json.password && req.body.Port === json.port && req.body.Database === json.dbName) { + req.reply({ + statusCode: 200, + }); + } else { + req.reply({ + statusCode: 400, + }); + } + }).as('testConnection'); + cy.get('#hostname-input').clear().type(json.hostname) + cy.get('#username-input').clear().type(json.username) + cy.get('#password-input').clear().type(json.password) + cy.get('#port-input').clear().type(json.port) + cy.get('#dbname-input').clear().type(json.dbName) + }) + + cy.get('#spanner-dialect-input').click(); + cy.fixture('constants').then((json) => { + cy.get('mat-option').contains(json.googleDialect).click(); + }) + + // Check if the button is enabled + cy.get('#test-connect-btn').should('be.enabled') + + // Submit the form + cy.get('#test-connect-btn').click(); + cy.get('#connect-btn', { timeout: 10000 }).should('be.enabled') + cy.get('#connect-btn').click(); + + // Check that workspace is rendered with correct number of tables in Object Viewer + cy.url().should('include', '/workspace'); + cy.fixture('mysql-config').then((json) => { + cy.get('#table-and-index-list').find('tbody tr').should('have.length', json.tableCount + 2); + }) + }); +}); \ No newline at end of file diff --git a/ui/cypress/fixtures/config.json b/ui/cypress/fixtures/config.json new file mode 100644 index 000000000..a34241612 --- /dev/null +++ b/ui/cypress/fixtures/config.json @@ -0,0 +1,4 @@ +{ + "projectId": "emulator-test-project", + "instanceId": "test-instance" +} diff --git a/ui/cypress/fixtures/constants.json b/ui/cypress/fixtures/constants.json new file mode 100644 index 000000000..998657865 --- /dev/null +++ b/ui/cypress/fixtures/constants.json @@ -0,0 +1,3 @@ +{ + "googleDialect": "Google Standard SQL Dialect" +} \ No newline at end of file diff --git a/ui/cypress/fixtures/mysql-config.json b/ui/cypress/fixtures/mysql-config.json new file mode 100644 index 000000000..2c22a667e --- /dev/null +++ b/ui/cypress/fixtures/mysql-config.json @@ -0,0 +1,8 @@ +{ + "hostname": "localhost", + "username": "root", + "password": "root", + "port": "3306", + "dbName": "test_interleave_table_data", + "tableCount": 1 +} \ No newline at end of file diff --git a/ui/dist/ui/index.html b/ui/dist/ui/index.html index e5676bee8..a58a0a959 100644 --- a/ui/dist/ui/index.html +++ b/ui/dist/ui/index.html @@ -12,5 +12,5 @@