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
def update
if @environment.update(environment_params)
redirect_to project_environment_path(project, @environment)
render json: { environment: @environment, path: project_environment_path(project, @environment) }
else
render :edit
render json: { message: @environment.errors.full_messages }, status: :bad_request
end
end
......
......@@ -45,6 +45,14 @@ module EnvironmentsHelper
can?(current_user, :destroy_environment, environment)
end
def environment_data(environment)
Gitlab::Json.generate({
id: environment.id,
name: environment.name,
external_url: environment.external_url
})
end
private
def project_metrics_data(project)
......
- page_title _("Edit"), @environment.name, _("Environments")
- add_page_specific_style 'page_bundles/environments'
%h3.page-title
= _('Edit environment')
%hr
= render 'form'
#js-edit-environment{ data: { project_environments_path: project_environments_path(@project),
update_environment_path: project_environment_path(@project, @environment),
environment: environment_data(@environment)} }
......@@ -200,11 +200,27 @@ RSpec.describe Projects::EnvironmentsController do
end
describe 'PATCH #update' do
it 'responds with a 302' do
patch_params = environment_params.merge(environment: { external_url: 'https://git.gitlab.com' })
patch :update, params: patch_params
subject { patch :update, params: 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
......
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
expect(helper.environment_logs_data(project, environment)).to eq(expected_data)
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
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