Commit 961d0d8a authored by Andrew Fontaine's avatar Andrew Fontaine

Migrate Edit Environments Form to Vue

Re-uses the new form component made as part of the migration of new
environments.

There was some question about whether or not we need Vuex or something,
but honestly because the form manages such a low number of properties
and the validation requirements are so small, that I opted to not use it
here instead.

Also the environment model is small, and so can be inlined in a data
attribute. As the validation is stricter on the backend, this should be
clear of HTML injection.

Changelog: changed
parent 070d28f0
<script>
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import EnvironmentForm from './environment_form.vue';
export default {
components: {
EnvironmentForm,
},
inject: ['projectEnvironmentsPath', 'updateEnvironmentPath'],
props: {
environment: {
required: true,
type: Object,
},
},
data() {
return {
formEnvironment: {
name: this.environment.name,
externalUrl: this.environment.external_url,
},
};
},
methods: {
onChange(environment) {
this.formEnvironment = environment;
},
onSubmit() {
axios
.put(this.updateEnvironmentPath, {
id: this.environment.id,
name: this.formEnvironment.name,
external_url: this.formEnvironment.externalUrl,
})
.then(({ data: { path } }) => visitUrl(path))
.catch((error) => {
const message = error.response.data.message[0];
createFlash({ message });
});
},
},
};
</script>
<template>
<environment-form
:cancel-path="projectEnvironmentsPath"
:environment="formEnvironment"
:title="__('Edit environment')"
@change="onChange"
@submit="onSubmit"
/>
</template>
import Vue from 'vue';
import EditEnvironment from './components/edit_environment.vue';
export default (el) =>
new Vue({
el,
provide: {
projectEnvironmentsPath: el.dataset.projectEnvironmentsPath,
updateEnvironmentPath: el.dataset.updateEnvironmentPath,
},
render(h) {
return h(EditEnvironment, {
props: {
environment: JSON.parse(el.dataset.environment),
},
});
},
});
import mountEdit from '~/environments/edit';
mountEdit(document.getElementById('js-edit-environment'));
...@@ -95,9 +95,9 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -95,9 +95,9 @@ class Projects::EnvironmentsController < Projects::ApplicationController
def update def update
if @environment.update(environment_params) if @environment.update(environment_params)
redirect_to project_environment_path(project, @environment) render json: { environment: @environment, path: project_environment_path(project, @environment) }
else else
render :edit render json: { message: @environment.errors.full_messages }, status: :bad_request
end end
end end
......
...@@ -45,6 +45,14 @@ module EnvironmentsHelper ...@@ -45,6 +45,14 @@ module EnvironmentsHelper
can?(current_user, :destroy_environment, environment) can?(current_user, :destroy_environment, environment)
end end
def environment_data(environment)
Gitlab::Json.generate({
id: environment.id,
name: environment.name,
external_url: environment.external_url
})
end
private private
def project_metrics_data(project) def project_metrics_data(project)
......
- page_title _("Edit"), @environment.name, _("Environments") - page_title _("Edit"), @environment.name, _("Environments")
- add_page_specific_style 'page_bundles/environments' - add_page_specific_style 'page_bundles/environments'
%h3.page-title #js-edit-environment{ data: { project_environments_path: project_environments_path(@project),
= _('Edit environment') update_environment_path: project_environment_path(@project, @environment),
%hr environment: environment_data(@environment)} }
= render 'form'
...@@ -200,11 +200,27 @@ RSpec.describe Projects::EnvironmentsController do ...@@ -200,11 +200,27 @@ RSpec.describe Projects::EnvironmentsController do
end end
describe 'PATCH #update' do describe 'PATCH #update' do
it 'responds with a 302' do subject { patch :update, params: params }
patch_params = environment_params.merge(environment: { external_url: 'https://git.gitlab.com' })
patch :update, params: patch_params
expect(response).to have_gitlab_http_status(:found) context "when environment params are valid" do
let(:params) { environment_params.merge(environment: { external_url: 'https://git.gitlab.com' }) }
it 'returns ok and the path to the newly created environment' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['path']).to eq("/#{project.full_path}/-/environments/#{environment.id}")
end
end
context "when environment params are invalid" do
let(:params) { environment_params.merge(environment: { name: '/foo/', external_url: '/git.gitlab.com' }) }
it 'returns bad request' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
end
end end
end end
......
import MockAdapter from 'axios-mock-adapter';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import EditEnvironment from '~/environments/components/edit_environment.vue';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
jest.mock('~/lib/utils/url_utility');
jest.mock('~/flash');
const DEFAULT_OPTS = {
provide: {
projectEnvironmentsPath: '/projects/environments',
updateEnvironmentPath: '/proejcts/environments/1',
},
propsData: { environment: { name: 'foo', externalUrl: 'https://foo.example.com' } },
};
describe('~/environments/components/edit.vue', () => {
let wrapper;
let mock;
let name;
let url;
let form;
const createWrapper = (opts = {}) =>
mountExtended(EditEnvironment, {
...DEFAULT_OPTS,
...opts,
});
beforeEach(() => {
mock = new MockAdapter(axios);
wrapper = createWrapper();
name = wrapper.findByLabelText('Name');
url = wrapper.findByLabelText('External URL');
form = wrapper.findByRole('form', { name: 'Edit environment' });
});
afterEach(() => {
mock.restore();
wrapper.destroy();
});
const fillForm = async (expected, response) => {
mock
.onPut(DEFAULT_OPTS.provide.updateEnvironmentPath, {
name: expected.name,
external_url: expected.url,
})
.reply(...response);
await name.setValue(expected.name);
await url.setValue(expected.url);
await form.trigger('submit');
await waitForPromises();
};
it('sets the title to Edit environment', () => {
const header = wrapper.findByRole('heading', { name: 'Edit environment' });
expect(header.exists()).toBe(true);
});
it.each`
input | value
${() => name} | ${'test'}
${() => url} | ${'https://example.org'}
`('it changes the value of the input to $value', async ({ input, value }) => {
await input().setValue(value);
expect(input().element.value).toBe(value);
});
it('submits the updated environment on submit', async () => {
const expected = { name: 'test', url: 'https://google.ca' };
await fillForm(expected, [200, { path: '/test' }]);
expect(visitUrl).toHaveBeenCalledWith('/test');
});
it('shows errors on error', async () => {
const expected = { name: 'test', url: 'https://google.ca' };
await fillForm(expected, [400, { message: ['name taken'] }]);
expect(createFlash).toHaveBeenCalledWith({ message: 'name taken' });
});
});
...@@ -199,4 +199,13 @@ RSpec.describe EnvironmentsHelper do ...@@ -199,4 +199,13 @@ RSpec.describe EnvironmentsHelper do
expect(helper.environment_logs_data(project, environment)).to eq(expected_data) expect(helper.environment_logs_data(project, environment)).to eq(expected_data)
end end
end end
describe '#environment_data' do
it 'returns the environment as JSON' do
expected_data = { id: environment.id,
name: environment.name,
external_url: environment.external_url }.to_json
expect(helper.environment_data(environment)).to eq(expected_data)
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment