diff --git a/app/assets/javascripts/google_cloud/components/cloudsql/create_instance_form.vue b/app/assets/javascripts/google_cloud/components/cloudsql/create_instance_form.vue new file mode 100644 index 0000000000000000000000000000000000000000..0ac561b6132c397d21747b0e68c61a2005046146 --- /dev/null +++ b/app/assets/javascripts/google_cloud/components/cloudsql/create_instance_form.vue @@ -0,0 +1,132 @@ +<script> +import { GlButton, GlFormCheckbox, GlFormGroup, GlFormSelect } from '@gitlab/ui'; +import { s__ } from '~/locale'; + +const i18n = { + gcpProjectLabel: s__('CloudSeed|Google Cloud project'), + gcpProjectDescription: s__( + 'CloudSeed|Database instance is generated within the selected Google Cloud project', + ), + refsLabel: s__('CloudSeed|Refs'), + refsDescription: s__( + 'CloudSeed|Generated database instance is linked to the selected branch or tag', + ), + databaseVersionLabel: s__('CloudSeed|Database version'), + tierLabel: s__('CloudSeed|Machine type'), + tierDescription: s__('CloudSeed|Determines memory and virtual cores available to your instance'), + checkboxLabel: s__( + 'CloudSeed|I accept Google Cloud pricing and responsibilities involved with managing database instances', + ), + cancelLabel: s__('CloudSeed|Cancel'), + submitLabel: s__('CloudSeed|Create instance'), + all: s__('CloudSeed|All'), +}; + +export default { + ALL_REFS: '*', + components: { + GlButton, + GlFormCheckbox, + GlFormGroup, + GlFormSelect, + }, + props: { + cancelPath: { required: true, type: String }, + gcpProjects: { required: true, type: Array }, + refs: { required: true, type: Array }, + formTitle: { required: true, type: String }, + formDescription: { required: true, type: String }, + databaseVersions: { required: true, type: Array }, + tiers: { required: true, type: Array }, + }, + i18n, +}; +</script> +<template> + <div> + <header class="gl-my-5 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid"> + <h2 class="gl-font-size-h1">{{ formTitle }}</h2> + <p>{{ formDescription }}</p> + </header> + + <gl-form-group + data-testid="form_group_gcp_project" + label-for="gcp_project" + :label="$options.i18n.gcpProjectLabel" + :description="$options.i18n.gcpProjectDescription" + > + <gl-form-select id="gcp_project" data-testid="select_gcp_project" name="gcp_project" required> + <option + v-for="gcpProject in gcpProjects" + :key="gcpProject.project_id" + :value="gcpProject.project_id" + > + {{ gcpProject.name }} + </option> + </gl-form-select> + </gl-form-group> + + <gl-form-group + data-testid="form_group_environments" + label-for="ref" + :label="$options.i18n.refsLabel" + :description="$options.i18n.refsDescription" + > + <gl-form-select id="ref" data-testid="select_environments" name="ref" required> + <option :value="$options.ALL_REFS">{{ $options.i18n.all }}</option> + <option v-for="ref in refs" :key="ref" :value="ref"> + {{ ref }} + </option> + </gl-form-select> + </gl-form-group> + + <gl-form-group + data-testid="form_group_tier" + label-for="tier" + :label="$options.i18n.tierLabel" + :description="$options.i18n.tierDescription" + > + <gl-form-select id="tier" data-testid="select_tier" name="tier" required> + <option v-for="tier in tiers" :key="tier.value" :value="tier.value"> + {{ tier.label }} + </option> + </gl-form-select> + </gl-form-group> + + <gl-form-group + data-testid="form_group_database_version" + label-for="database-version" + :label="$options.i18n.databaseVersionLabel" + > + <gl-form-select + id="database-version" + data-testid="select_database_version" + name="database_version" + required + > + <option + v-for="databaseVersion in databaseVersions" + :key="databaseVersion.value" + :value="databaseVersion.value" + > + {{ databaseVersion.label }} + </option> + </gl-form-select> + </gl-form-group> + + <gl-form-group> + <gl-form-checkbox name="confirmation" required> + {{ $options.i18n.checkboxLabel }} + </gl-form-checkbox> + </gl-form-group> + + <div class="form-actions row"> + <gl-button type="submit" category="primary" variant="confirm" data-testid="submit-button"> + {{ $options.i18n.submitLabel }} + </gl-button> + <gl-button class="gl-ml-1" :href="cancelPath" data-testid="cancel-button">{{ + $options.i18n.cancelLabel + }}</gl-button> + </div> + </div> +</template> diff --git a/app/assets/javascripts/google_cloud/components/cloudsql/instance_table.vue b/app/assets/javascripts/google_cloud/components/cloudsql/instance_table.vue new file mode 100644 index 0000000000000000000000000000000000000000..823895214dfa217943abb41e82d4261966a96f06 --- /dev/null +++ b/app/assets/javascripts/google_cloud/components/cloudsql/instance_table.vue @@ -0,0 +1,75 @@ +<script> +import { GlEmptyState, GlLink, GlTable } from '@gitlab/ui'; +import { encodeSaferUrl, setUrlParams } from '~/lib/utils/url_utility'; +import { s__ } from '~/locale'; + +const i18n = { + noInstancesTitle: s__('CloudSeed|No instances'), + noInstancesDescription: s__('CloudSeed|There are no instances to display.'), + title: s__('CloudSeed|Instances'), + description: s__('CloudSeed|Database instances associated with this project'), +}; + +export default { + components: { GlEmptyState, GlLink, GlTable }, + props: { + cloudsqlInstances: { + type: Array, + required: true, + }, + emptyIllustrationUrl: { + type: String, + required: true, + }, + }, + computed: { + tableData() { + return this.cloudsqlInstances.filter((instance) => instance.instance_name); + }, + }, + methods: { + gcpProjectUrl(id) { + return setUrlParams({ project: id }, 'https://console.cloud.google.com/sql/instances'); + }, + instanceUrl(name, id) { + const saferName = encodeSaferUrl(name); + + return setUrlParams( + { project: id }, + `https://console.cloud.google.com/sql/instances/${saferName}/overview`, + ); + }, + }, + fields: [ + { key: 'ref', label: s__('CloudSeed|Environment') }, + { key: 'gcp_project', label: s__('CloudSeed|Google Cloud Project') }, + { key: 'instance_name', label: s__('CloudSeed|CloudSQL Instance') }, + { key: 'version', label: s__('CloudSeed|Version') }, + ], + i18n, +}; +</script> + +<template> + <div class="gl-mx-3"> + <gl-empty-state + v-if="tableData.length === 0" + :title="$options.i18n.noInstancesTitle" + :description="$options.i18n.noInstancesDescription" + :svg-path="emptyIllustrationUrl" + /> + + <div v-else> + <h2 class="gl-font-size-h2">{{ $options.i18n.title }}</h2> + <p>{{ $options.i18n.description }}</p> + <gl-table :fields="$options.fields" :items="tableData"> + <template #cell(gcp_project)="{ value }"> + <gl-link :href="gcpProjectUrl(value)">{{ value }}</gl-link> + </template> + <template #cell(instance_name)="{ item: { instance_name, gcp_project } }"> + <a :href="instanceUrl(instance_name, gcp_project)">{{ instance_name }}</a> + </template> + </gl-table> + </div> + </div> +</template> diff --git a/app/assets/javascripts/google_cloud/components/databases/service_table.vue b/app/assets/javascripts/google_cloud/components/databases/service_table.vue new file mode 100644 index 0000000000000000000000000000000000000000..80bd6ef28fb28ed437b5e8d4b27c8de9faf5fe47 --- /dev/null +++ b/app/assets/javascripts/google_cloud/components/databases/service_table.vue @@ -0,0 +1,221 @@ +<script> +import { GlAlert, GlButton, GlLink, GlSprintf, GlTable } from '@gitlab/ui'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import { s__ } from '~/locale'; + +const KEY_CLOUDSQL_POSTGRES = 'cloudsql-postgres'; +const KEY_CLOUDSQL_MYSQL = 'cloudsql-mysql'; +const KEY_CLOUDSQL_SQLSERVER = 'cloudsql-sqlserver'; +const KEY_ALLOYDB_POSTGRES = 'alloydb-postgres'; +const KEY_MEMORYSTORE_REDIS = 'memorystore-redis'; +const KEY_FIRESTORE = 'firestore'; + +const i18n = { + columnService: s__('CloudSeed|Service'), + columnDescription: s__('CloudSeed|Description'), + cloudsqlPostgresTitle: s__('CloudSeed|Cloud SQL for Postgres'), + cloudsqlPostgresDescription: s__( + 'CloudSeed|Fully managed relational database service for PostgreSQL', + ), + cloudsqlMysqlTitle: s__('CloudSeed|Cloud SQL for MySQL'), + cloudsqlMysqlDescription: s__('CloudSeed|Fully managed relational database service for MySQL'), + cloudsqlSqlserverTitle: s__('CloudSeed|Cloud SQL for SQL Server'), + cloudsqlSqlserverDescription: s__( + 'CloudSeed|Fully managed relational database service for SQL Server', + ), + alloydbPostgresTitle: s__('CloudSeed|AlloyDB for Postgres'), + alloydbPostgresDescription: s__( + 'CloudSeed|Fully managed PostgreSQL-compatible service for high-demand workloads', + ), + memorystoreRedisTitle: s__('CloudSeed|Memorystore for Redis'), + memorystoreRedisDescription: s__( + 'CloudSeed|Scalable, secure, and highly available in-memory service for Redis', + ), + firestoreTitle: s__('CloudSeed|Cloud Firestore'), + firestoreDescription: s__( + 'CloudSeed|Flexible, scalable NoSQL cloud database for client- and server-side development', + ), + createInstance: s__('CloudSeed|Create instance'), + createCluster: s__('CloudSeed|Create cluster'), + createDatabase: s__('CloudSeed|Create database'), + title: s__('CloudSeed|Services'), + description: s__('CloudSeed|Available database services through which instances may be created'), + pricingAlert: s__( + 'CloudSeed|Learn more about pricing for %{cloudsqlPricingStart}Cloud SQL%{cloudsqlPricingEnd}, %{alloydbPricingStart}Alloy DB%{alloydbPricingEnd}, %{memorystorePricingStart}Memorystore%{memorystorePricingEnd} and %{firestorePricingStart}Firestore%{firestorePricingEnd}.', + ), + secretManagersDescription: s__( + 'CloudSeed|Enhance security by storing database variables in secret managers - learn more about %{docLinkStart}secret management with GitLab%{docLinkEnd}', + ), +}; + +const helpUrlSecrets = helpPagePath('ee/ci/secrets'); + +export default { + components: { GlAlert, GlButton, GlLink, GlSprintf, GlTable }, + props: { + cloudsqlPostgresUrl: { + type: String, + required: true, + }, + cloudsqlMysqlUrl: { + type: String, + required: true, + }, + cloudsqlSqlserverUrl: { + type: String, + required: true, + }, + alloydbPostgresUrl: { + type: String, + required: true, + }, + memorystoreRedisUrl: { + type: String, + required: true, + }, + firestoreUrl: { + type: String, + required: true, + }, + }, + methods: { + actionUrl(key) { + switch (key) { + case KEY_CLOUDSQL_POSTGRES: + return this.cloudsqlPostgresUrl; + case KEY_CLOUDSQL_MYSQL: + return this.cloudsqlMysqlUrl; + case KEY_CLOUDSQL_SQLSERVER: + return this.cloudsqlSqlserverUrl; + case KEY_ALLOYDB_POSTGRES: + return this.alloydbPostgresUrl; + case KEY_MEMORYSTORE_REDIS: + return this.memorystoreRedisUrl; + case KEY_FIRESTORE: + return this.firestoreUrl; + default: + return '#'; + } + }, + }, + fields: [ + { key: 'title', label: i18n.columnService }, + { key: 'description', label: i18n.columnDescription }, + { key: 'action', label: '' }, + ], + items: [ + { + title: i18n.cloudsqlPostgresTitle, + description: i18n.cloudsqlPostgresDescription, + action: { + key: KEY_CLOUDSQL_POSTGRES, + title: i18n.createInstance, + testId: 'button-cloudsql-postgres', + }, + }, + { + title: i18n.cloudsqlMysqlTitle, + description: i18n.cloudsqlMysqlDescription, + action: { + disabled: false, + key: KEY_CLOUDSQL_MYSQL, + title: i18n.createInstance, + testId: 'button-cloudsql-mysql', + }, + }, + { + title: i18n.cloudsqlSqlserverTitle, + description: i18n.cloudsqlSqlserverDescription, + action: { + disabled: false, + key: KEY_CLOUDSQL_SQLSERVER, + title: i18n.createInstance, + testId: 'button-cloudsql-sqlserver', + }, + }, + { + title: i18n.alloydbPostgresTitle, + description: i18n.alloydbPostgresDescription, + action: { + disabled: true, + key: KEY_ALLOYDB_POSTGRES, + title: i18n.createCluster, + testId: 'button-alloydb-postgres', + }, + }, + { + title: i18n.memorystoreRedisTitle, + description: i18n.memorystoreRedisDescription, + action: { + disabled: true, + key: KEY_MEMORYSTORE_REDIS, + title: i18n.createInstance, + testId: 'button-memorystore-redis', + }, + }, + { + title: i18n.firestoreTitle, + description: i18n.firestoreDescription, + action: { + disabled: true, + key: KEY_FIRESTORE, + title: i18n.createDatabase, + testId: 'button-firestore', + }, + }, + ], + helpUrlSecrets, + i18n, +}; +</script> + +<template> + <div class="gl-mx-3"> + <h2 class="gl-font-size-h2">{{ $options.i18n.title }}</h2> + <p>{{ $options.i18n.description }}</p> + + <gl-table :fields="$options.fields" :items="$options.items"> + <template #cell(action)="{ value }"> + <gl-button + block + :disabled="value.disabled" + :href="actionUrl(value.key)" + :data-testid="value.testId" + category="secondary" + variant="confirm" + > + {{ value.title }} + </gl-button> + </template> + </gl-table> + + <gl-alert class="gl-mt-5" :dismissible="false" variant="tip"> + <gl-sprintf :message="$options.i18n.pricingAlert"> + <template #cloudsqlPricing="{ content }"> + <gl-link href="https://cloud.google.com/sql/pricing">{{ content }}</gl-link> + </template> + <template #alloydbPricing="{ content }"> + <gl-link href="https://cloud.google.com/alloydb/pricing">{{ content }}</gl-link> + </template> + <template #memorystorePricing="{ content }"> + <gl-link href="https://cloud.google.com/memorystore/docs/redis/pricing">{{ + content + }}</gl-link> + </template> + <template #firestorePricing="{ content }"> + <gl-link href="https://cloud.google.com/firestore/pricing">{{ content }}</gl-link> + </template> + </gl-sprintf> + </gl-alert> + + <gl-alert class="gl-mt-5" :dismissible="false" variant="tip"> + <gl-sprintf :message="$options.i18n.secretManagersDescription"> + <template #docLink="{ content }"> + <gl-link :href="$options.helpUrlSecrets"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </gl-alert> + </div> +</template> diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e02822d4617727bf7a09333ccc46a469d48fb015..eda8c83fe0836ec30d3e013141dc21dfd24410b4 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8255,6 +8255,123 @@ msgstr "" msgid "Cloud Storage" msgstr "" +msgid "CloudSeed|All" +msgstr "" + +msgid "CloudSeed|AlloyDB for Postgres" +msgstr "" + +msgid "CloudSeed|Available database services through which instances may be created" +msgstr "" + +msgid "CloudSeed|Cancel" +msgstr "" + +msgid "CloudSeed|Cloud Firestore" +msgstr "" + +msgid "CloudSeed|Cloud SQL for MySQL" +msgstr "" + +msgid "CloudSeed|Cloud SQL for Postgres" +msgstr "" + +msgid "CloudSeed|Cloud SQL for SQL Server" +msgstr "" + +msgid "CloudSeed|CloudSQL Instance" +msgstr "" + +msgid "CloudSeed|Create cluster" +msgstr "" + +msgid "CloudSeed|Create database" +msgstr "" + +msgid "CloudSeed|Create instance" +msgstr "" + +msgid "CloudSeed|Database instance is generated within the selected Google Cloud project" +msgstr "" + +msgid "CloudSeed|Database instances associated with this project" +msgstr "" + +msgid "CloudSeed|Database version" +msgstr "" + +msgid "CloudSeed|Description" +msgstr "" + +msgid "CloudSeed|Determines memory and virtual cores available to your instance" +msgstr "" + +msgid "CloudSeed|Enhance security by storing database variables in secret managers - learn more about %{docLinkStart}secret management with GitLab%{docLinkEnd}" +msgstr "" + +msgid "CloudSeed|Environment" +msgstr "" + +msgid "CloudSeed|Flexible, scalable NoSQL cloud database for client- and server-side development" +msgstr "" + +msgid "CloudSeed|Fully managed PostgreSQL-compatible service for high-demand workloads" +msgstr "" + +msgid "CloudSeed|Fully managed relational database service for MySQL" +msgstr "" + +msgid "CloudSeed|Fully managed relational database service for PostgreSQL" +msgstr "" + +msgid "CloudSeed|Fully managed relational database service for SQL Server" +msgstr "" + +msgid "CloudSeed|Generated database instance is linked to the selected branch or tag" +msgstr "" + +msgid "CloudSeed|Google Cloud Project" +msgstr "" + +msgid "CloudSeed|Google Cloud project" +msgstr "" + +msgid "CloudSeed|I accept Google Cloud pricing and responsibilities involved with managing database instances" +msgstr "" + +msgid "CloudSeed|Instances" +msgstr "" + +msgid "CloudSeed|Learn more about pricing for %{cloudsqlPricingStart}Cloud SQL%{cloudsqlPricingEnd}, %{alloydbPricingStart}Alloy DB%{alloydbPricingEnd}, %{memorystorePricingStart}Memorystore%{memorystorePricingEnd} and %{firestorePricingStart}Firestore%{firestorePricingEnd}." +msgstr "" + +msgid "CloudSeed|Machine type" +msgstr "" + +msgid "CloudSeed|Memorystore for Redis" +msgstr "" + +msgid "CloudSeed|No instances" +msgstr "" + +msgid "CloudSeed|Refs" +msgstr "" + +msgid "CloudSeed|Scalable, secure, and highly available in-memory service for Redis" +msgstr "" + +msgid "CloudSeed|Service" +msgstr "" + +msgid "CloudSeed|Services" +msgstr "" + +msgid "CloudSeed|There are no instances to display." +msgstr "" + +msgid "CloudSeed|Version" +msgstr "" + msgid "Cluster" msgstr "" diff --git a/spec/frontend/google_cloud/components/cloudsql/create_instance_form_spec.js b/spec/frontend/google_cloud/components/cloudsql/create_instance_form_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..de644a33b503afa61dc56acfa3f1009839558437 --- /dev/null +++ b/spec/frontend/google_cloud/components/cloudsql/create_instance_form_spec.js @@ -0,0 +1,103 @@ +import { GlFormCheckbox } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import InstanceForm from '~/google_cloud/components/cloudsql/create_instance_form.vue'; + +describe('google_cloud::cloudsql::create_instance_form component', () => { + let wrapper; + + const findByTestId = (id) => wrapper.findByTestId(id); + const findCancelButton = () => findByTestId('cancel-button'); + const findCheckbox = () => wrapper.findComponent(GlFormCheckbox); + const findHeader = () => wrapper.find('header'); + const findSubmitButton = () => findByTestId('submit-button'); + + const propsData = { + gcpProjects: [], + refs: [], + cancelPath: '#cancel-url', + formTitle: 'mock form title', + formDescription: 'mock form description', + databaseVersions: [], + tiers: [], + }; + + beforeEach(() => { + wrapper = shallowMountExtended(InstanceForm, { propsData, stubs: { GlFormCheckbox } }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('contains header', () => { + expect(findHeader().exists()).toBe(true); + }); + + it('contains GCP project form group', () => { + const formGroup = findByTestId('form_group_gcp_project'); + expect(formGroup.exists()).toBe(true); + expect(formGroup.attributes('label')).toBe(InstanceForm.i18n.gcpProjectLabel); + expect(formGroup.attributes('description')).toBe(InstanceForm.i18n.gcpProjectDescription); + }); + + it('contains GCP project dropdown', () => { + const select = findByTestId('select_gcp_project'); + expect(select.exists()).toBe(true); + }); + + it('contains Environments form group', () => { + const formGroup = findByTestId('form_group_environments'); + expect(formGroup.exists()).toBe(true); + expect(formGroup.attributes('label')).toBe(InstanceForm.i18n.refsLabel); + expect(formGroup.attributes('description')).toBe(InstanceForm.i18n.refsDescription); + }); + + it('contains Environments dropdown', () => { + const select = findByTestId('select_environments'); + expect(select.exists()).toBe(true); + }); + + it('contains Tier form group', () => { + const formGroup = findByTestId('form_group_tier'); + expect(formGroup.exists()).toBe(true); + expect(formGroup.attributes('label')).toBe(InstanceForm.i18n.tierLabel); + expect(formGroup.attributes('description')).toBe(InstanceForm.i18n.tierDescription); + }); + + it('contains Tier dropdown', () => { + const select = findByTestId('select_tier'); + expect(select.exists()).toBe(true); + }); + + it('contains Database Version form group', () => { + const formGroup = findByTestId('form_group_database_version'); + expect(formGroup.exists()).toBe(true); + expect(formGroup.attributes('label')).toBe(InstanceForm.i18n.databaseVersionLabel); + }); + + it('contains Database Version dropdown', () => { + const select = findByTestId('select_database_version'); + expect(select.exists()).toBe(true); + }); + + it('contains Submit button', () => { + expect(findSubmitButton().exists()).toBe(true); + expect(findSubmitButton().text()).toBe(InstanceForm.i18n.submitLabel); + }); + + it('contains Cancel button', () => { + expect(findCancelButton().exists()).toBe(true); + expect(findCancelButton().text()).toBe(InstanceForm.i18n.cancelLabel); + expect(findCancelButton().attributes('href')).toBe('#cancel-url'); + }); + + it('contains Confirmation checkbox', () => { + const checkbox = findCheckbox(); + expect(checkbox.text()).toBe(InstanceForm.i18n.checkboxLabel); + }); + + it('checkbox must be required', () => { + const checkbox = findCheckbox(); + expect(checkbox.attributes('required')).toBe('true'); + }); +}); diff --git a/spec/frontend/google_cloud/components/cloudsql/instance_table_spec.js b/spec/frontend/google_cloud/components/cloudsql/instance_table_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..286f2b8e37938cb8e1956b1a2d645fbd37d90ca2 --- /dev/null +++ b/spec/frontend/google_cloud/components/cloudsql/instance_table_spec.js @@ -0,0 +1,65 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlEmptyState, GlTable } from '@gitlab/ui'; +import InstanceTable from '~/google_cloud/components/cloudsql/instance_table.vue'; + +describe('google_cloud::databases::service_table component', () => { + let wrapper; + + const findEmptyState = () => wrapper.findComponent(GlEmptyState); + const findTable = () => wrapper.findComponent(GlTable); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('when there are no instances', () => { + beforeEach(() => { + const propsData = { + cloudsqlInstances: [], + emptyIllustrationUrl: '#empty-illustration-url', + }; + wrapper = shallowMount(InstanceTable, { propsData }); + }); + + it('should depict empty state', () => { + const emptyState = findEmptyState(); + expect(emptyState.exists()).toBe(true); + expect(emptyState.attributes('title')).toBe(InstanceTable.i18n.noInstancesTitle); + expect(emptyState.attributes('description')).toBe(InstanceTable.i18n.noInstancesDescription); + }); + }); + + describe('when there are three instances', () => { + beforeEach(() => { + const propsData = { + cloudsqlInstances: [ + { + ref: '*', + gcp_project: 'test-gcp-project', + instance_name: 'postgres-14-instance', + version: 'POSTGRES_14', + }, + { + ref: 'production', + gcp_project: 'prod-gcp-project', + instance_name: 'postgres-14-instance', + version: 'POSTGRES_14', + }, + { + ref: 'staging', + gcp_project: 'test-gcp-project', + instance_name: 'postgres-14-instance', + version: 'POSTGRES_14', + }, + ], + emptyIllustrationUrl: '#empty-illustration-url', + }; + wrapper = shallowMount(InstanceTable, { propsData }); + }); + + it('should contain a table', () => { + const table = findTable(); + expect(table.exists()).toBe(true); + }); + }); +}); diff --git a/spec/frontend/google_cloud/components/databases/service_table_spec.js b/spec/frontend/google_cloud/components/databases/service_table_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..142e32c1a4bfeda27e4a6e1b78d59af03f9e25d1 --- /dev/null +++ b/spec/frontend/google_cloud/components/databases/service_table_spec.js @@ -0,0 +1,44 @@ +import { GlTable } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import ServiceTable from '~/google_cloud/components/databases/service_table.vue'; + +describe('google_cloud::databases::service_table component', () => { + let wrapper; + + const findTable = () => wrapper.findComponent(GlTable); + + beforeEach(() => { + const propsData = { + cloudsqlPostgresUrl: '#url-cloudsql-postgres', + cloudsqlMysqlUrl: '#url-cloudsql-mysql', + cloudsqlSqlserverUrl: '#url-cloudsql-sqlserver', + alloydbPostgresUrl: '#url-alloydb-postgres', + memorystoreRedisUrl: '#url-memorystore-redis', + firestoreUrl: '#url-firestore', + }; + wrapper = mountExtended(ServiceTable, { propsData }); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('should contain a table', () => { + expect(findTable().exists()).toBe(true); + }); + + it.each` + name | testId | url + ${'cloudsql-postgres'} | ${'button-cloudsql-postgres'} | ${'#url-cloudsql-postgres'} + ${'cloudsql-mysql'} | ${'button-cloudsql-mysql'} | ${'#url-cloudsql-mysql'} + ${'cloudsql-sqlserver'} | ${'button-cloudsql-sqlserver'} | ${'#url-cloudsql-sqlserver'} + ${'alloydb-postgres'} | ${'button-alloydb-postgres'} | ${'#url-alloydb-postgres'} + ${'memorystore-redis'} | ${'button-memorystore-redis'} | ${'#url-memorystore-redis'} + ${'firestore'} | ${'button-firestore'} | ${'#url-firestore'} + `('renders $name button with correct url', ({ testId, url }) => { + const button = wrapper.findByTestId(testId); + + expect(button.exists()).toBe(true); + expect(button.attributes('href')).toBe(url); + }); +});