Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-12-08 00:12:55 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-12-08 00:12:55 +0300
commit2cf4bdd0b060175c7058b395014b101fbe6214a3 (patch)
treef3aaeca09cc434632c8eb2c2998877c3f9a2e0d9 /spec/frontend
parent1bdc6c89c32a7380a81598629b9ad05ba9a2a94f (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/environments/environment_flux_resource_selector_spec.js2
-rw-r--r--spec/frontend/environments/environment_form_spec.js168
-rw-r--r--spec/frontend/environments/environment_namespace_selector_spec.js217
-rw-r--r--spec/frontend/environments/folder/environments_folder_app_spec.js47
-rw-r--r--spec/frontend/environments/graphql/mock_data.js150
-rw-r--r--spec/frontend/environments/graphql/resolvers/base_spec.js29
-rw-r--r--spec/frontend/ide/init_gitlab_web_ide_spec.js28
-rw-r--r--spec/frontend/ide/lib/gitlab_web_ide/get_oauth_config_spec.js16
-rw-r--r--spec/frontend/ide/mount_oauth_callback_spec.js53
9 files changed, 570 insertions, 140 deletions
diff --git a/spec/frontend/environments/environment_flux_resource_selector_spec.js b/spec/frontend/environments/environment_flux_resource_selector_spec.js
index ba3375c731f..8dab8fdd96a 100644
--- a/spec/frontend/environments/environment_flux_resource_selector_spec.js
+++ b/spec/frontend/environments/environment_flux_resource_selector_spec.js
@@ -25,7 +25,7 @@ const DEFAULT_PROPS = {
fluxResourcePath: '',
};
-describe('~/environments/components/form.vue', () => {
+describe('~/environments/components/flux_resource_selector.vue', () => {
let wrapper;
const kustomizationItem = {
diff --git a/spec/frontend/environments/environment_form_spec.js b/spec/frontend/environments/environment_form_spec.js
index 478ac8d6e0e..f3dfc7a72f2 100644
--- a/spec/frontend/environments/environment_form_spec.js
+++ b/spec/frontend/environments/environment_form_spec.js
@@ -1,11 +1,12 @@
-import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
-import Vue from 'vue';
+import { GlLoadingIcon } from '@gitlab/ui';
+import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import EnvironmentForm from '~/environments/components/environment_form.vue';
import getUserAuthorizedAgents from '~/environments/graphql/queries/user_authorized_agents.query.graphql';
import EnvironmentFluxResourceSelector from '~/environments/components/environment_flux_resource_selector.vue';
+import EnvironmentNamespaceSelector from '~/environments/components/environment_namespace_selector.vue';
import createMockApollo from '../__helpers__/mock_apollo_helper';
import { mockKasTunnelUrl } from './mock_data';
@@ -36,13 +37,16 @@ const configuration = {
credentials: 'include',
};
+const environmentWithAgentAndNamespace = {
+ ...DEFAULT_PROPS.environment,
+ clusterAgent: { id: '12', name: 'agent-2' },
+ clusterAgentId: '2',
+ kubernetesNamespace: 'agent',
+};
+
describe('~/environments/components/form.vue', () => {
let wrapper;
- const getNamespacesQueryResult = jest
- .fn()
- .mockReturnValue([{ metadata: { name: 'default' } }, { metadata: { name: 'agent' } }]);
-
const createWrapper = (propsData = {}, options = {}) =>
mountExtended(EnvironmentForm, {
provide: PROVIDE,
@@ -53,7 +57,7 @@ describe('~/environments/components/form.vue', () => {
},
});
- const createWrapperWithApollo = ({ propsData = {}, queryResult = null } = {}) => {
+ const createWrapperWithApollo = (propsData = {}) => {
Vue.use(VueApollo);
const requestHandlers = [
@@ -70,12 +74,6 @@ describe('~/environments/components/form.vue', () => {
],
];
- const mockResolvers = {
- Query: {
- k8sNamespaces: queryResult || getNamespacesQueryResult,
- },
- };
-
return mountExtended(EnvironmentForm, {
provide: {
...PROVIDE,
@@ -84,13 +82,12 @@ describe('~/environments/components/form.vue', () => {
...DEFAULT_PROPS,
...propsData,
},
- apolloProvider: createMockApollo(requestHandlers, mockResolvers),
+ apolloProvider: createMockApollo(requestHandlers, []),
});
};
const findAgentSelector = () => wrapper.findByTestId('agent-selector');
- const findNamespaceSelector = () => wrapper.findByTestId('namespace-selector');
- const findAlert = () => wrapper.findComponent(GlAlert);
+ const findNamespaceSelector = () => wrapper.findComponent(EnvironmentNamespaceSelector);
const findFluxResourceSelector = () => wrapper.findComponent(EnvironmentFluxResourceSelector);
const selectAgent = async () => {
@@ -326,91 +323,15 @@ describe('~/environments/components/form.vue', () => {
expect(findNamespaceSelector().exists()).toBe(true);
});
- it('requests the kubernetes namespaces with the correct configuration', async () => {
- await waitForPromises();
-
- expect(getNamespacesQueryResult).toHaveBeenCalledWith(
- {},
- { configuration },
- expect.anything(),
- expect.anything(),
- );
- });
-
- it('sets the loading prop while fetching the list', async () => {
- expect(findNamespaceSelector().props('loading')).toBe(true);
-
- await waitForPromises();
-
- expect(findNamespaceSelector().props('loading')).toBe(false);
- });
-
- it('renders a list of available namespaces', async () => {
- await waitForPromises();
-
- expect(findNamespaceSelector().props('items')).toEqual([
- { text: 'default', value: 'default' },
- { text: 'agent', value: 'agent' },
- ]);
- });
-
- it('filters the namespaces list on user search', async () => {
- await waitForPromises();
- await findNamespaceSelector().vm.$emit('search', 'default');
-
- expect(findNamespaceSelector().props('items')).toEqual([
- { value: 'default', text: 'default' },
- ]);
- });
-
- it('updates namespace selector field with the name of selected namespace', async () => {
- await waitForPromises();
- await findNamespaceSelector().vm.$emit('select', 'agent');
-
- expect(findNamespaceSelector().props('toggleText')).toBe('agent');
- });
-
it('emits changes to the kubernetesNamespace', async () => {
await waitForPromises();
- await findNamespaceSelector().vm.$emit('select', 'agent');
+ findNamespaceSelector().vm.$emit('change', 'agent');
+ await nextTick();
expect(wrapper.emitted('change')[1]).toEqual([
{ name: '', externalUrl: '', kubernetesNamespace: 'agent', fluxResourcePath: null },
]);
});
-
- it('clears namespace selector when another agent was selected', async () => {
- await waitForPromises();
- await findNamespaceSelector().vm.$emit('select', 'agent');
-
- expect(findNamespaceSelector().props('toggleText')).toBe('agent');
-
- await findAgentSelector().vm.$emit('select', '1');
- expect(findNamespaceSelector().props('toggleText')).toBe(
- EnvironmentForm.i18n.namespaceHelpText,
- );
- });
- });
-
- describe('when cannot connect to the cluster', () => {
- const error = new Error('Error from the cluster_client API');
-
- beforeEach(async () => {
- wrapper = createWrapperWithApollo({
- queryResult: jest.fn().mockRejectedValueOnce(error),
- });
-
- await selectAgent();
- await waitForPromises();
- });
-
- it("doesn't render the namespace selector", () => {
- expect(findNamespaceSelector().exists()).toBe(false);
- });
-
- it('renders an alert', () => {
- expect(findAlert().text()).toBe('Error from the cluster_client API');
- });
});
});
@@ -431,16 +352,6 @@ describe('~/environments/components/form.vue', () => {
it("doesn't render flux resource selector", () => {
expect(findFluxResourceSelector().exists()).toBe(false);
});
-
- it('renders the flux resource selector when the namespace is selected', async () => {
- await findNamespaceSelector().vm.$emit('select', 'agent');
-
- expect(findFluxResourceSelector().props()).toEqual({
- namespace: 'agent',
- fluxResourcePath: '',
- configuration,
- });
- });
});
});
@@ -451,9 +362,7 @@ describe('~/environments/components/form.vue', () => {
clusterAgentId: '1',
};
beforeEach(() => {
- wrapper = createWrapperWithApollo({
- propsData: { environment: environmentWithAgent },
- });
+ wrapper = createWrapperWithApollo({ environment: environmentWithAgent });
});
it('updates agent selector field with the name of the associated agent', () => {
@@ -468,45 +377,46 @@ describe('~/environments/components/form.vue', () => {
it('renders a list of available namespaces', async () => {
await waitForPromises();
- expect(findNamespaceSelector().props('items')).toEqual([
- { text: 'default', value: 'default' },
- { text: 'agent', value: 'agent' },
- ]);
+ expect(findNamespaceSelector().exists()).toBe(true);
});
});
describe('when environment has an associated kubernetes namespace', () => {
- const environmentWithAgentAndNamespace = {
- ...DEFAULT_PROPS.environment,
- clusterAgent: { id: '1', name: 'agent-1' },
- clusterAgentId: '1',
- kubernetesNamespace: 'default',
- };
beforeEach(() => {
- wrapper = createWrapperWithApollo({
- propsData: { environment: environmentWithAgentAndNamespace },
- });
+ wrapper = createWrapperWithApollo({ environment: environmentWithAgentAndNamespace });
});
it('updates namespace selector with the name of the associated namespace', async () => {
await waitForPromises();
- expect(findNamespaceSelector().props('toggleText')).toBe('default');
+ expect(findNamespaceSelector().props('namespace')).toBe('agent');
+ });
+
+ it('clears namespace selector when another agent was selected', async () => {
+ expect(findNamespaceSelector().props('namespace')).toBe('agent');
+
+ findAgentSelector().vm.$emit('select', '1');
+ await nextTick();
+
+ expect(findNamespaceSelector().props('namespace')).toBe(null);
+ });
+
+ it('renders the flux resource selector when the namespace is selected', () => {
+ expect(findFluxResourceSelector().props()).toEqual({
+ namespace: 'agent',
+ fluxResourcePath: '',
+ configuration,
+ });
});
});
describe('when environment has an associated flux resource', () => {
const fluxResourcePath = 'path/to/flux/resource';
- const environmentWithAgentAndNamespace = {
- ...DEFAULT_PROPS.environment,
- clusterAgent: { id: '1', name: 'agent-1' },
- clusterAgentId: '1',
- kubernetesNamespace: 'default',
+ const environmentWithFluxResource = {
+ ...environmentWithAgentAndNamespace,
fluxResourcePath,
};
beforeEach(() => {
- wrapper = createWrapperWithApollo({
- propsData: { environment: environmentWithAgentAndNamespace },
- });
+ wrapper = createWrapperWithApollo({ environment: environmentWithFluxResource });
});
it('provides flux resource path to the flux resource selector component', () => {
diff --git a/spec/frontend/environments/environment_namespace_selector_spec.js b/spec/frontend/environments/environment_namespace_selector_spec.js
new file mode 100644
index 00000000000..53e4f807751
--- /dev/null
+++ b/spec/frontend/environments/environment_namespace_selector_spec.js
@@ -0,0 +1,217 @@
+import { GlAlert, GlCollapsibleListbox, GlButton } from '@gitlab/ui';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import { shallowMount } from '@vue/test-utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import EnvironmentNamespaceSelector from '~/environments/components/environment_namespace_selector.vue';
+import { stubComponent } from 'helpers/stub_component';
+import createMockApollo from '../__helpers__/mock_apollo_helper';
+import { mockKasTunnelUrl } from './mock_data';
+
+const configuration = {
+ basePath: mockKasTunnelUrl.replace(/\/$/, ''),
+ headers: {
+ 'GitLab-Agent-Id': 2,
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ },
+ credentials: 'include',
+};
+
+const DEFAULT_PROPS = {
+ namespace: '',
+ configuration,
+};
+
+describe('~/environments/components/namespace_selector.vue', () => {
+ let wrapper;
+
+ const getNamespacesQueryResult = jest
+ .fn()
+ .mockReturnValue([
+ { metadata: { name: 'default' } },
+ { metadata: { name: 'agent' } },
+ { metadata: { name: 'test-agent' } },
+ ]);
+
+ const closeMock = jest.fn();
+
+ const createWrapper = ({ propsData = {}, queryResult = null } = {}) => {
+ Vue.use(VueApollo);
+
+ const mockResolvers = {
+ Query: {
+ k8sNamespaces: queryResult || getNamespacesQueryResult,
+ },
+ };
+
+ return shallowMount(EnvironmentNamespaceSelector, {
+ propsData: {
+ ...DEFAULT_PROPS,
+ ...propsData,
+ },
+ stubs: {
+ GlCollapsibleListbox: stubComponent(GlCollapsibleListbox, {
+ template: `<div><slot name="footer"></slot></div>`,
+ methods: {
+ close: closeMock,
+ },
+ }),
+ },
+ apolloProvider: createMockApollo([], mockResolvers),
+ });
+ };
+
+ const findNamespaceSelector = () => wrapper.findComponent(GlCollapsibleListbox);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findSelectButton = () => wrapper.findComponent(GlButton);
+
+ const searchNamespace = async (searchTerm = 'test') => {
+ findNamespaceSelector().vm.$emit('search', searchTerm);
+ await nextTick();
+ };
+
+ describe('default', () => {
+ beforeEach(() => {
+ wrapper = createWrapper();
+ });
+
+ it('renders namespace selector', () => {
+ expect(findNamespaceSelector().exists()).toBe(true);
+ });
+
+ it('requests the namespaces', async () => {
+ await waitForPromises();
+
+ expect(getNamespacesQueryResult).toHaveBeenCalled();
+ });
+
+ it('sets the loading prop while fetching the list', async () => {
+ expect(findNamespaceSelector().props('loading')).toBe(true);
+
+ await waitForPromises();
+
+ expect(findNamespaceSelector().props('loading')).toBe(false);
+ });
+
+ it('renders a list of available namespaces', async () => {
+ await waitForPromises();
+
+ expect(findNamespaceSelector().props('items')).toMatchObject([
+ {
+ text: 'default',
+ value: 'default',
+ },
+ {
+ text: 'agent',
+ value: 'agent',
+ },
+ {
+ text: 'test-agent',
+ value: 'test-agent',
+ },
+ ]);
+ });
+
+ it('filters the namespaces list on user search', async () => {
+ await waitForPromises();
+ await searchNamespace('agent');
+
+ expect(findNamespaceSelector().props('items')).toMatchObject([
+ {
+ text: 'agent',
+ value: 'agent',
+ },
+ {
+ text: 'test-agent',
+ value: 'test-agent',
+ },
+ ]);
+ });
+
+ it('emits changes to the namespace', () => {
+ findNamespaceSelector().vm.$emit('select', 'agent');
+
+ expect(wrapper.emitted('change')).toEqual([['agent']]);
+ });
+ });
+
+ describe('custom select button', () => {
+ beforeEach(async () => {
+ wrapper = createWrapper();
+ await waitForPromises();
+ });
+
+ it("doesn't render custom select button before searching", () => {
+ expect(findSelectButton().exists()).toBe(false);
+ });
+
+ it("doesn't render custom select button when the search is found in the namespaces list", async () => {
+ await searchNamespace('test-agent');
+ expect(findSelectButton().exists()).toBe(false);
+ });
+
+ it('renders custom select button when the namespace searched for is not found in the namespaces list', async () => {
+ await searchNamespace();
+ expect(findSelectButton().exists()).toBe(true);
+ });
+
+ it('emits custom filled namespace name to the `change` event', async () => {
+ await searchNamespace();
+ findSelectButton().vm.$emit('click');
+
+ expect(wrapper.emitted('change')).toEqual([['test']]);
+ });
+
+ it('closes the listbox after the custom value for the namespace was selected', async () => {
+ await searchNamespace();
+ findSelectButton().vm.$emit('click');
+
+ expect(closeMock).toHaveBeenCalled();
+ });
+ });
+
+ describe('when environment has an associated namespace', () => {
+ beforeEach(() => {
+ wrapper = createWrapper({
+ propsData: { namespace: 'existing-namespace' },
+ });
+ });
+
+ it('updates namespace selector with the name of the associated namespace', () => {
+ expect(findNamespaceSelector().props('toggleText')).toBe('existing-namespace');
+ });
+ });
+
+ describe('on error', () => {
+ const error = new Error('Error from the cluster_client API');
+
+ beforeEach(async () => {
+ wrapper = createWrapper({
+ queryResult: jest.fn().mockRejectedValueOnce(error),
+ });
+ await waitForPromises();
+ });
+
+ it('renders an alert with the error text', () => {
+ expect(findAlert().text()).toContain(error.message);
+ });
+
+ it('renders an empty namespace selector', () => {
+ expect(findNamespaceSelector().props('items')).toMatchObject([]);
+ });
+
+ it('renders custom select button when the user performs search', async () => {
+ await searchNamespace();
+
+ expect(findSelectButton().exists()).toBe(true);
+ });
+
+ it('emits custom filled namespace name to the `change` event', async () => {
+ await searchNamespace();
+ findSelectButton().vm.$emit('click');
+
+ expect(wrapper.emitted('change')).toEqual([['test']]);
+ });
+ });
+});
diff --git a/spec/frontend/environments/folder/environments_folder_app_spec.js b/spec/frontend/environments/folder/environments_folder_app_spec.js
index 262e742ba5c..fbb252fb152 100644
--- a/spec/frontend/environments/folder/environments_folder_app_spec.js
+++ b/spec/frontend/environments/folder/environments_folder_app_spec.js
@@ -1,12 +1,21 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import { GlSkeletonLoader } from '@gitlab/ui';
+import { GlSkeletonLoader, GlTab } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import EnvironmentsFolderAppComponent from '~/environments/folder/environments_folder_app.vue';
import EnvironmentItem from '~/environments/components/new_environment_item.vue';
+import StopEnvironmentModal from '~/environments/components/stop_environment_modal.vue';
+import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue';
+import DeleteEnvironmentModal from '~/environments/components/delete_environment_modal.vue';
+import CanaryUpdateModal from '~/environments/components/canary_update_modal.vue';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import { resolvedFolder } from '../graphql/mock_data';
+import {
+ resolvedFolder,
+ resolvedEnvironment,
+ resolvedEnvironmentToDelete,
+ resolvedEnvironmentToRollback,
+} from '../graphql/mock_data';
Vue.use(VueApollo);
@@ -20,6 +29,11 @@ describe('EnvironmentsFolderAppComponent', () => {
const mockResolvers = {
Query: {
folder: environmentFolderMock,
+ environmentToDelete: jest.fn().mockReturnValue(resolvedEnvironmentToDelete),
+ environmentToRollback: jest.fn().mockReturnValue(resolvedEnvironment),
+ environmentToChangeCanary: jest.fn().mockReturnValue(resolvedEnvironment),
+ environmentToStop: jest.fn().mockReturnValue(resolvedEnvironment),
+ weight: jest.fn().mockReturnValue(1),
},
};
@@ -47,6 +61,7 @@ describe('EnvironmentsFolderAppComponent', () => {
propsData: {
folderName: mockFolderName,
folderPath: '/gitlab-org/test-project/-/environments/folder/dev',
+ scope: 'active',
},
});
};
@@ -54,6 +69,7 @@ describe('EnvironmentsFolderAppComponent', () => {
const findHeader = () => wrapper.findByTestId('folder-name');
const findEnvironmentItems = () => wrapper.findAllComponents(EnvironmentItem);
const findSkeletonLoaders = () => wrapper.findAllComponents(GlSkeletonLoader);
+ const findTabs = () => wrapper.findAllComponents(GlTab);
it('should render a header with the folder name', () => {
createWrapper();
@@ -76,5 +92,32 @@ describe('EnvironmentsFolderAppComponent', () => {
const items = findEnvironmentItems();
expect(items.length).toBe(resolvedFolder.environments.length);
});
+
+ it('should render active and stopped tabs', () => {
+ const tabs = findTabs();
+ expect(tabs.length).toBe(2);
+ });
+
+ [
+ [StopEnvironmentModal, resolvedEnvironment],
+ [DeleteEnvironmentModal, resolvedEnvironmentToDelete],
+ [ConfirmRollbackModal, resolvedEnvironmentToRollback],
+ ].forEach(([Component, expectedEnvironment]) =>
+ it(`should render ${Component.name} component`, () => {
+ const modal = wrapper.findComponent(Component);
+
+ expect(modal.exists()).toBe(true);
+ expect(modal.props().environment).toEqual(expectedEnvironment);
+ expect(modal.props().graphql).toBe(true);
+ }),
+ );
+
+ it(`should render CanaryUpdateModal component`, () => {
+ const modal = wrapper.findComponent(CanaryUpdateModal);
+
+ expect(modal.exists()).toBe(true);
+ expect(modal.props().environment).toEqual(resolvedEnvironment);
+ expect(modal.props().weight).toBe(1);
+ });
});
});
diff --git a/spec/frontend/environments/graphql/mock_data.js b/spec/frontend/environments/graphql/mock_data.js
index 7f5e46f51ad..efc63a80e89 100644
--- a/spec/frontend/environments/graphql/mock_data.js
+++ b/spec/frontend/environments/graphql/mock_data.js
@@ -930,3 +930,153 @@ export const fluxKustomizationsMock = [
];
export const fluxResourcePathMock = 'path/to/flux/resource';
+
+export const resolvedEnvironmentToDelete = {
+ __typename: 'LocalEnvironment',
+ id: 41,
+ name: 'review/hello',
+ deletePath: '/api/v4/projects/8/environments/41',
+};
+
+export const resolvedEnvironmentToRollback = {
+ __typename: 'LocalEnvironment',
+ id: 41,
+ name: 'review/hello',
+ lastDeployment: {
+ id: 78,
+ iid: 24,
+ sha: 'f3ba6dd84f8f891373e9b869135622b954852db1',
+ ref: { name: 'main', refPath: '/h5bp/html5-boilerplate/-/tree/main' },
+ status: 'success',
+ createdAt: '2022-01-07T15:47:27.415Z',
+ deployedAt: '2022-01-07T15:47:32.450Z',
+ tierInYaml: 'staging',
+ tag: false,
+ isLast: true,
+ user: {
+ id: 1,
+ username: 'root',
+ name: 'Administrator',
+ state: 'active',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ webUrl: 'http://gck.test:3000/root',
+ showStatus: false,
+ path: '/root',
+ },
+ deployable: {
+ id: 1014,
+ name: 'deploy-prod',
+ started: '2022-01-07T15:47:31.037Z',
+ complete: true,
+ archived: false,
+ buildPath: '/h5bp/html5-boilerplate/-/jobs/1014',
+ retryPath: '/h5bp/html5-boilerplate/-/jobs/1014/retry',
+ playable: false,
+ scheduled: false,
+ createdAt: '2022-01-07T15:47:27.404Z',
+ updatedAt: '2022-01-07T15:47:32.341Z',
+ status: {
+ action: {
+ buttonTitle: 'Retry this job',
+ icon: 'retry',
+ method: 'post',
+ path: '/h5bp/html5-boilerplate/-/jobs/1014/retry',
+ title: 'Retry',
+ },
+ detailsPath: '/h5bp/html5-boilerplate/-/jobs/1014',
+ favicon:
+ '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
+ group: 'success',
+ hasDetails: true,
+ icon: 'status_success',
+ illustration: {
+ image:
+ '/assets/illustrations/skipped-job_empty-29a8a37d8a61d1b6f68cf3484f9024e53cd6eb95e28eae3554f8011a1146bf27.svg',
+ size: 'svg-430',
+ title: 'This job does not have a trace.',
+ },
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ },
+ commit: {
+ id: 'f3ba6dd84f8f891373e9b869135622b954852db1',
+ shortId: 'f3ba6dd8',
+ createdAt: '2022-01-07T15:47:26.000+00:00',
+ parentIds: ['3213b6ac17afab99be37d5d38f38c6c8407387cc'],
+ title: 'Update .gitlab-ci.yml file',
+ message: 'Update .gitlab-ci.yml file',
+ authorName: 'Administrator',
+ authorEmail: 'admin@example.com',
+ authoredDate: '2022-01-07T15:47:26.000+00:00',
+ committerName: 'Administrator',
+ committerEmail: 'admin@example.com',
+ committedDate: '2022-01-07T15:47:26.000+00:00',
+ trailers: {},
+ webUrl:
+ 'http://gck.test:3000/h5bp/html5-boilerplate/-/commit/f3ba6dd84f8f891373e9b869135622b954852db1',
+ author: {
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ id: 1,
+ name: 'Administrator',
+ path: '/root',
+ showStatus: false,
+ state: 'active',
+ username: 'root',
+ webUrl: 'http://gck.test:3000/root',
+ },
+ authorGravatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ commitUrl:
+ 'http://gck.test:3000/h5bp/html5-boilerplate/-/commit/f3ba6dd84f8f891373e9b869135622b954852db1',
+ commitPath: '/h5bp/html5-boilerplate/-/commit/f3ba6dd84f8f891373e9b869135622b954852db1',
+ },
+ manualActions: [
+ {
+ id: 1015,
+ name: 'deploy-staging',
+ started: null,
+ complete: false,
+ archived: false,
+ buildPath: '/h5bp/html5-boilerplate/-/jobs/1015',
+ playPath: '/h5bp/html5-boilerplate/-/jobs/1015/play',
+ playable: true,
+ scheduled: false,
+ createdAt: '2022-01-07T15:47:27.422Z',
+ updatedAt: '2022-01-07T15:47:28.557Z',
+ status: {
+ icon: 'status_manual',
+ text: 'manual',
+ label: 'manual play action',
+ group: 'manual',
+ tooltip: 'manual action',
+ hasDetails: true,
+ detailsPath: '/h5bp/html5-boilerplate/-/jobs/1015',
+ illustration: {
+ image:
+ '/assets/illustrations/manual_action-c55aee2c5f9ebe9f72751480af8bb307be1a6f35552f344cc6d1bf979d3422f6.svg',
+ size: 'svg-394',
+ title: 'This job requires a manual action',
+ content:
+ 'This job requires manual intervention to start. Before starting this job, you can add variables below for last-minute configuration changes.',
+ },
+ favicon:
+ '/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png',
+ action: {
+ icon: 'play',
+ title: 'Play',
+ path: '/h5bp/html5-boilerplate/-/jobs/1015/play',
+ method: 'post',
+ buttonTitle: 'Run job',
+ },
+ },
+ },
+ ],
+ scheduledActions: [],
+ cluster: null,
+ },
+ retryUrl: '/h5bp/html5-boilerplate/-/jobs/1014/retry',
+};
diff --git a/spec/frontend/environments/graphql/resolvers/base_spec.js b/spec/frontend/environments/graphql/resolvers/base_spec.js
index e01cf18c40d..244c86fa679 100644
--- a/spec/frontend/environments/graphql/resolvers/base_spec.js
+++ b/spec/frontend/environments/graphql/resolvers/base_spec.js
@@ -147,10 +147,10 @@ describe('~/frontend/environments/graphql/resolvers', () => {
describe('stopEnvironmentREST', () => {
it('should post to the stop environment path', async () => {
mock.onPost(ENDPOINT).reply(HTTP_STATUS_OK);
-
+ const cache = { evict: jest.fn() };
const client = { writeQuery: jest.fn() };
const environment = { stopPath: ENDPOINT };
- await mockResolvers.Mutation.stopEnvironmentREST(null, { environment }, { client });
+ await mockResolvers.Mutation.stopEnvironmentREST(null, { environment }, { client, cache });
expect(mock.history.post).toContainEqual(
expect.objectContaining({ url: ENDPOINT, method: 'post' }),
@@ -161,6 +161,7 @@ describe('~/frontend/environments/graphql/resolvers', () => {
variables: { environment },
data: { isEnvironmentStopping: true },
});
+ expect(cache.evict).toHaveBeenCalledWith({ fieldName: 'folder' });
});
it('should set is stopping to false if stop fails', async () => {
mock.onPost(ENDPOINT).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
@@ -183,27 +184,39 @@ describe('~/frontend/environments/graphql/resolvers', () => {
describe('rollbackEnvironment', () => {
it('should post to the retry environment path', async () => {
mock.onPost(ENDPOINT).reply(HTTP_STATUS_OK);
+ const cache = { evict: jest.fn() };
- await mockResolvers.Mutation.rollbackEnvironment(null, {
- environment: { retryUrl: ENDPOINT },
- });
+ await mockResolvers.Mutation.rollbackEnvironment(
+ null,
+ {
+ environment: { retryUrl: ENDPOINT },
+ },
+ { cache },
+ );
expect(mock.history.post).toContainEqual(
expect.objectContaining({ url: ENDPOINT, method: 'post' }),
);
+ expect(cache.evict).toHaveBeenCalledWith({ fieldName: 'folder' });
});
});
describe('deleteEnvironment', () => {
it('should DELETE to the delete environment path', async () => {
mock.onDelete(ENDPOINT).reply(HTTP_STATUS_OK);
+ const cache = { evict: jest.fn() };
- await mockResolvers.Mutation.deleteEnvironment(null, {
- environment: { deletePath: ENDPOINT },
- });
+ await mockResolvers.Mutation.deleteEnvironment(
+ null,
+ {
+ environment: { deletePath: ENDPOINT },
+ },
+ { cache },
+ );
expect(mock.history.delete).toContainEqual(
expect.objectContaining({ url: ENDPOINT, method: 'delete' }),
);
+ expect(cache.evict).toHaveBeenCalledWith({ fieldName: 'folder' });
});
});
describe('cancelAutoStop', () => {
diff --git a/spec/frontend/ide/init_gitlab_web_ide_spec.js b/spec/frontend/ide/init_gitlab_web_ide_spec.js
index 6a5bedb0bbb..d7a16bec1c3 100644
--- a/spec/frontend/ide/init_gitlab_web_ide_spec.js
+++ b/spec/frontend/ide/init_gitlab_web_ide_spec.js
@@ -40,6 +40,9 @@ const TEST_EDITOR_FONT_SRC_URL = 'http://gitlab.test/assets/gitlab-mono/GitLabMo
const TEST_EDITOR_FONT_FORMAT = 'woff2';
const TEST_EDITOR_FONT_FAMILY = 'GitLab Mono';
+const TEST_OAUTH_CLIENT_ID = 'oauth-client-id-123abc';
+const TEST_OAUTH_CALLBACK_URL = 'https://example.com/oauth_callback';
+
describe('ide/init_gitlab_web_ide', () => {
let resolveConfirm;
@@ -231,4 +234,29 @@ describe('ide/init_gitlab_web_ide', () => {
);
});
});
+
+ describe('when oauth info is in dataset', () => {
+ beforeEach(() => {
+ findRootElement().dataset.clientId = TEST_OAUTH_CLIENT_ID;
+ findRootElement().dataset.callbackUrl = TEST_OAUTH_CALLBACK_URL;
+
+ createSubject();
+ });
+
+ it('calls start with element', () => {
+ expect(start).toHaveBeenCalledTimes(1);
+ expect(start).toHaveBeenCalledWith(
+ findRootElement(),
+ expect.objectContaining({
+ auth: {
+ type: 'oauth',
+ clientId: TEST_OAUTH_CLIENT_ID,
+ callbackUrl: TEST_OAUTH_CALLBACK_URL,
+ protectRefreshToken: true,
+ },
+ httpHeaders: undefined,
+ }),
+ );
+ });
+ });
});
diff --git a/spec/frontend/ide/lib/gitlab_web_ide/get_oauth_config_spec.js b/spec/frontend/ide/lib/gitlab_web_ide/get_oauth_config_spec.js
new file mode 100644
index 00000000000..3431068937f
--- /dev/null
+++ b/spec/frontend/ide/lib/gitlab_web_ide/get_oauth_config_spec.js
@@ -0,0 +1,16 @@
+import { getOAuthConfig } from '~/ide/lib/gitlab_web_ide/get_oauth_config';
+
+describe('~/ide/lib/gitlab_web_ide/get_oauth_config', () => {
+ it('returns undefined if no clientId found', () => {
+ expect(getOAuthConfig({})).toBeUndefined();
+ });
+
+ it('returns auth config from dataset', () => {
+ expect(getOAuthConfig({ clientId: 'test-clientId', callbackUrl: 'test-callbackUrl' })).toEqual({
+ type: 'oauth',
+ clientId: 'test-clientId',
+ callbackUrl: 'test-callbackUrl',
+ protectRefreshToken: true,
+ });
+ });
+});
diff --git a/spec/frontend/ide/mount_oauth_callback_spec.js b/spec/frontend/ide/mount_oauth_callback_spec.js
new file mode 100644
index 00000000000..6ac0b4e4615
--- /dev/null
+++ b/spec/frontend/ide/mount_oauth_callback_spec.js
@@ -0,0 +1,53 @@
+import { oauthCallback } from '@gitlab/web-ide';
+import { TEST_HOST } from 'helpers/test_constants';
+import { mountOAuthCallback } from '~/ide/mount_oauth_callback';
+
+jest.mock('@gitlab/web-ide');
+
+const TEST_USERNAME = 'gandalf.the.grey';
+const TEST_GITLAB_WEB_IDE_PUBLIC_PATH = 'test/webpack/assets/gitlab-web-ide/public/path';
+
+const TEST_OAUTH_CLIENT_ID = 'oauth-client-id-123abc';
+const TEST_OAUTH_CALLBACK_URL = 'https://example.com/oauth_callback';
+
+describe('~/ide/mount_oauth_callback', () => {
+ const createRootElement = () => {
+ const el = document.createElement('div');
+
+ el.id = 'ide';
+ el.dataset.clientId = TEST_OAUTH_CLIENT_ID;
+ el.dataset.callbackUrl = TEST_OAUTH_CALLBACK_URL;
+
+ document.body.append(el);
+ };
+
+ beforeEach(() => {
+ gon.current_username = TEST_USERNAME;
+ process.env.GITLAB_WEB_IDE_PUBLIC_PATH = TEST_GITLAB_WEB_IDE_PUBLIC_PATH;
+
+ createRootElement();
+ });
+
+ afterEach(() => {
+ document.body.innerHTML = '';
+ });
+
+ it('calls oauthCallback', () => {
+ expect(oauthCallback).not.toHaveBeenCalled();
+
+ mountOAuthCallback();
+
+ expect(oauthCallback).toHaveBeenCalledTimes(1);
+ expect(oauthCallback).toHaveBeenCalledWith({
+ auth: {
+ type: 'oauth',
+ callbackUrl: TEST_OAUTH_CALLBACK_URL,
+ clientId: TEST_OAUTH_CLIENT_ID,
+ protectRefreshToken: true,
+ },
+ gitlabUrl: TEST_HOST,
+ baseUrl: `${TEST_HOST}/${TEST_GITLAB_WEB_IDE_PUBLIC_PATH}`,
+ username: TEST_USERNAME,
+ });
+ });
+});