Commit 2a7690c5 authored by Andrew Fontaine's avatar Andrew Fontaine

Remove new environments table feature flag

Cleans up the feature tests for the environmetns page as well, making
sure they pass with the new table.

Changes are shockingly minimal, which is nice.

Changelog: changed
parent ccd20df3
...@@ -302,7 +302,11 @@ export default { ...@@ -302,7 +302,11 @@ export default {
class="gl-pl-4" class="gl-pl-4"
/> />
</div> </div>
<div v-if="upcomingDeployment" :class="$options.deploymentClasses"> <div
v-if="upcomingDeployment"
:class="$options.deploymentClasses"
data-testid="upcoming-deployment-content"
>
<deployment <deployment
:deployment="upcomingDeployment" :deployment="upcomingDeployment"
:class="{ 'gl-ml-7': inFolder }" :class="{ 'gl-ml-7': inFolder }"
......
...@@ -16,12 +16,14 @@ import EnvironmentItem from './new_environment_item.vue'; ...@@ -16,12 +16,14 @@ import EnvironmentItem from './new_environment_item.vue';
import ConfirmRollbackModal from './confirm_rollback_modal.vue'; import ConfirmRollbackModal from './confirm_rollback_modal.vue';
import DeleteEnvironmentModal from './delete_environment_modal.vue'; import DeleteEnvironmentModal from './delete_environment_modal.vue';
import CanaryUpdateModal from './canary_update_modal.vue'; import CanaryUpdateModal from './canary_update_modal.vue';
import EmptyState from './empty_state.vue';
export default { export default {
components: { components: {
DeleteEnvironmentModal, DeleteEnvironmentModal,
CanaryUpdateModal, CanaryUpdateModal,
ConfirmRollbackModal, ConfirmRollbackModal,
EmptyState,
EnvironmentFolder, EnvironmentFolder,
EnableReviewAppModal, EnableReviewAppModal,
EnvironmentItem, EnvironmentItem,
...@@ -66,7 +68,7 @@ export default { ...@@ -66,7 +68,7 @@ export default {
query: environmentToChangeCanaryQuery, query: environmentToChangeCanaryQuery,
}, },
}, },
inject: ['newEnvironmentPath', 'canCreateEnvironment'], inject: ['newEnvironmentPath', 'canCreateEnvironment', 'helpPagePath'],
i18n: { i18n: {
newEnvironmentButtonLabel: s__('Environments|New environment'), newEnvironmentButtonLabel: s__('Environments|New environment'),
reviewAppButtonLabel: s__('Environments|Enable review app'), reviewAppButtonLabel: s__('Environments|Enable review app'),
...@@ -103,6 +105,9 @@ export default { ...@@ -103,6 +105,9 @@ export default {
environments() { environments() {
return this.environmentApp?.environments?.filter((e) => e.size === 1) ?? []; return this.environmentApp?.environments?.filter((e) => e.size === 1) ?? [];
}, },
hasEnvironments() {
return this.environments.length > 0 || this.folders.length > 0;
},
availableCount() { availableCount() {
return this.environmentApp?.availableCount; return this.environmentApp?.availableCount;
}, },
...@@ -221,19 +226,23 @@ export default { ...@@ -221,19 +226,23 @@ export default {
</template> </template>
</gl-tab> </gl-tab>
</gl-tabs> </gl-tabs>
<environment-folder <template v-if="hasEnvironments">
v-for="folder in folders" <environment-folder
:key="folder.name" v-for="folder in folders"
class="gl-mb-3" :key="folder.name"
:nested-environment="folder" class="gl-mb-3"
/> :scope="scope"
<environment-item :nested-environment="folder"
v-for="environment in environments" />
:key="environment.name" <environment-item
class="gl-mb-3 gl-border-gray-100 gl-border-1 gl-border-b-solid" v-for="environment in environments"
:environment="environment.latest" :key="environment.name"
@change="resetPolling" class="gl-mb-3 gl-border-gray-100 gl-border-1 gl-border-b-solid"
/> :environment="environment.latest"
@change="resetPolling"
/>
</template>
<empty-state v-else :help-path="helpPagePath" />
<gl-pagination <gl-pagination
align="center" align="center"
:total-items="totalItems" :total-items="totalItems"
......
- page_title _("Environments") - page_title _("Environments")
- add_page_specific_style 'page_bundles/environments' - add_page_specific_style 'page_bundles/environments'
- if Feature.enabled?(:new_environments_table) #environments-table{ data: { endpoint: project_environments_path(@project, format: :json),
#environments-table{ data: { endpoint: project_environments_path(@project, format: :json), "can-read-environment" => can?(current_user, :read_environment, @project).to_s,
"can-read-environment" => can?(current_user, :read_environment, @project).to_s, "can-create-environment" => can?(current_user, :create_environment, @project).to_s,
"can-create-environment" => can?(current_user, :create_environment, @project).to_s, "new-environment-path" => new_project_environment_path(@project),
"new-environment-path" => new_project_environment_path(@project), "help-page-path" => help_page_path("ci/environments/index.md"),
"help-page-path" => help_page_path("ci/environments/index.md"), "project-path" => @project.full_path,
"project-path" => @project.full_path, "project-id" => @project.id,
"project-id" => @project.id, "default-branch-name" => @project.default_branch_or_main } }
"default-branch-name" => @project.default_branch_or_main } }
- else
#environments-list-view{ data: { environments_data: environments_list_data,
"can-read-environment" => can?(current_user, :read_environment, @project).to_s,
"can-create-environment" => can?(current_user, :create_environment, @project).to_s,
"new-environment-path" => new_project_environment_path(@project),
"help-page-path" => help_page_path("ci/environments/index.md"),
"project-path" => @project.full_path,
"project-id" => @project.id,
"default-branch-name" => @project.default_branch_or_main } }
---
name: new_environments_table
introduced_by_url:
rollout_issue_url:
milestone: '14.4'
type: development
group: group::release
default_enabled: false
...@@ -216,6 +216,8 @@ RSpec.describe 'Environments page', :js do ...@@ -216,6 +216,8 @@ RSpec.describe 'Environments page', :js do
it 'shows the open alert for the environment row' do it 'shows the open alert for the environment row' do
visit project_environments_path(project) visit project_environments_path(project)
page.click_button _('Expand')
within(find('div[data-testid="alert"]')) do within(find('div[data-testid="alert"]')) do
expect(page).to have_content('Critical') expect(page).to have_content('Critical')
expect(page).to have_content('HTTP Error Rate exceeded 1.0%') expect(page).to have_content('HTTP Error Rate exceeded 1.0%')
......
...@@ -10,7 +10,6 @@ RSpec.describe 'Environments page', :js do ...@@ -10,7 +10,6 @@ RSpec.describe 'Environments page', :js do
let(:role) { :developer } let(:role) { :developer }
before do before do
stub_feature_flags(new_environments_table: false)
project.add_role(user, role) project.add_role(user, role)
sign_in(user) sign_in(user)
end end
...@@ -35,24 +34,18 @@ RSpec.describe 'Environments page', :js do ...@@ -35,24 +34,18 @@ RSpec.describe 'Environments page', :js do
it 'shows "Available" and "Stopped" tab with links' do it 'shows "Available" and "Stopped" tab with links' do
visit_environments(project) visit_environments(project)
expect(page).to have_selector('.js-environments-tab-available') expect(page).to have_link(_('Available'))
expect(page).to have_content('Available') expect(page).to have_link(_('Stopped'))
expect(page).to have_selector('.js-environments-tab-stopped')
expect(page).to have_content('Stopped')
end end
describe 'with one available environment' do describe 'with one available environment' do
before do let!(:environment) { create(:environment, project: project, state: :available) }
create(:environment, project: project, state: :available)
end
describe 'in available tab page' do describe 'in available tab page' do
it 'shows one environment' do it 'shows one environment' do
visit_environments(project, scope: 'available') visit_environments(project, scope: 'available')
expect(page).to have_css('.environments-container') expect(page).to have_link(environment.name, href: project_environment_path(project, environment))
expect(page.all('.environment-name').length).to eq(1)
expect(page.all('[data-testid="stop-icon"]').length).to eq(1)
end end
end end
...@@ -77,7 +70,6 @@ RSpec.describe 'Environments page', :js do ...@@ -77,7 +70,6 @@ RSpec.describe 'Environments page', :js do
it 'shows no environments' do it 'shows no environments' do
visit_environments(project, scope: 'stopped') visit_environments(project, scope: 'stopped')
expect(page).to have_css('.environments-container')
expect(page).to have_content('You don\'t have any environments right now') expect(page).to have_content('You don\'t have any environments right now')
end end
end end
...@@ -95,22 +87,18 @@ RSpec.describe 'Environments page', :js do ...@@ -95,22 +87,18 @@ RSpec.describe 'Environments page', :js do
it 'shows one environment without error' do it 'shows one environment without error' do
visit_environments(project, scope: 'available') visit_environments(project, scope: 'available')
expect(page).to have_css('.environments-container') expect(page).to have_link(environment.name, href: project_environment_path(project, environment))
expect(page.all('.environment-name').length).to eq(1)
end end
end end
end end
describe 'with one stopped environment' do describe 'with one stopped environment' do
before do let!(:environment) { create(:environment, project: project, state: :stopped) }
create(:environment, project: project, state: :stopped)
end
describe 'in available tab page' do describe 'in available tab page' do
it 'shows no environments' do it 'shows no environments' do
visit_environments(project, scope: 'available') visit_environments(project, scope: 'available')
expect(page).to have_css('.environments-container')
expect(page).to have_content('You don\'t have any environments right now') expect(page).to have_content('You don\'t have any environments right now')
end end
end end
...@@ -119,8 +107,7 @@ RSpec.describe 'Environments page', :js do ...@@ -119,8 +107,7 @@ RSpec.describe 'Environments page', :js do
it 'shows one environment' do it 'shows one environment' do
visit_environments(project, scope: 'stopped') visit_environments(project, scope: 'stopped')
expect(page).to have_css('.environments-container') expect(page).to have_link(environment.name, href: project_environment_path(project, environment))
expect(page.all('.environment-name').length).to eq(1)
expect(page.all('[data-testid="stop-icon"]').length).to eq(0) expect(page.all('[data-testid="stop-icon"]').length).to eq(0)
end end
end end
...@@ -135,8 +122,8 @@ RSpec.describe 'Environments page', :js do ...@@ -135,8 +122,8 @@ RSpec.describe 'Environments page', :js do
it 'does not show environments and counters are set to zero' do it 'does not show environments and counters are set to zero' do
expect(page).to have_content('You don\'t have any environments right now') expect(page).to have_content('You don\'t have any environments right now')
expect(page.find('.js-environments-tab-available .badge').text).to eq('0') expect(page).to have_link("#{_('Available')} 0")
expect(page.find('.js-environments-tab-stopped .badge').text).to eq('0') expect(page).to have_link("#{_('Stopped')} 0")
end end
end end
...@@ -150,21 +137,23 @@ RSpec.describe 'Environments page', :js do ...@@ -150,21 +137,23 @@ RSpec.describe 'Environments page', :js do
context 'when there are no deployments' do context 'when there are no deployments' do
before do before do
visit_environments(project) visit_environments(project)
page.click_button _('Expand')
end end
it 'shows environments names and counters' do it 'shows environments names and counters' do
expect(page).to have_link(environment.name) expect(page).to have_link(environment.name, href: project_environment_path(project, environment))
expect(page.find('.js-environments-tab-available .badge').text).to eq('1') expect(page).to have_link("#{_('Available')} 1")
expect(page.find('.js-environments-tab-stopped .badge').text).to eq('0') expect(page).to have_link("#{_('Stopped')} 0")
end end
it 'does not show deployments' do it 'does not show deployments' do
expect(page).to have_content('No deployments yet') expect(page).to have_content(s_('Environments|There are no deployments for this environment yet. Learn more about setting up deployments.'))
end end
it 'shows stop button when environment is not stoppable' do it 'shows stop button when environment is not stoppable' do
expect(page).to have_selector(stop_button_selector) expect(page).to have_button('Stop')
end end
end end
...@@ -179,8 +168,10 @@ RSpec.describe 'Environments page', :js do ...@@ -179,8 +168,10 @@ RSpec.describe 'Environments page', :js do
it 'shows deployment SHA and internal ID' do it 'shows deployment SHA and internal ID' do
visit_environments(project) visit_environments(project)
page.click_button _('Expand')
expect(page).to have_link(deployment.short_sha) expect(page).to have_text(deployment.short_sha)
expect(page).to have_link(deployment.commit.full_title)
expect(page).to have_content(deployment.iid) expect(page).to have_content(deployment.iid)
end end
...@@ -218,10 +209,6 @@ RSpec.describe 'Environments page', :js do ...@@ -218,10 +209,6 @@ RSpec.describe 'Environments page', :js do
.not_to change { Ci::Pipeline.count } .not_to change { Ci::Pipeline.count }
end end
it 'shows build name and id' do
expect(page).to have_link("#{build.name} ##{build.id}")
end
it 'shows a stop button' do it 'shows a stop button' do
expect(page).to have_selector(stop_button_selector) expect(page).to have_selector(stop_button_selector)
end end
...@@ -373,7 +360,8 @@ RSpec.describe 'Environments page', :js do ...@@ -373,7 +360,8 @@ RSpec.describe 'Environments page', :js do
it 'does not show deployments' do it 'does not show deployments' do
visit_environments(project) visit_environments(project)
expect(page).to have_content('No deployments yet') page.click_button _('Expand')
expect(page).to have_content(s_('Environments|There are no deployments for this environment yet. Learn more about setting up deployments.'))
end end
end end
...@@ -389,9 +377,10 @@ RSpec.describe 'Environments page', :js do ...@@ -389,9 +377,10 @@ RSpec.describe 'Environments page', :js do
it "renders the upcoming deployment", :aggregate_failures do it "renders the upcoming deployment", :aggregate_failures do
visit_environments(project) visit_environments(project)
page.click_button _('Expand')
within(upcoming_deployment_content_selector) do within(upcoming_deployment_content_selector) do
expect(page).to have_content("##{deployment.iid}") expect(page).to have_content("##{deployment.iid}")
expect(page).to have_selector("a[href=\"#{project_job_path(project, deployment.deployable)}\"]")
expect(page).to have_link(href: /#{deployment.user.username}/) expect(page).to have_link(href: /#{deployment.user.username}/)
end end
end end
...@@ -413,15 +402,15 @@ RSpec.describe 'Environments page', :js do ...@@ -413,15 +402,15 @@ RSpec.describe 'Environments page', :js do
let(:role) { :developer } let(:role) { :developer }
it 'developer creates a new environment with a valid name' do it 'developer creates a new environment with a valid name' do
within(".environments-section") { click_link 'New environment' } click_link 'New environment'
fill_in('Name', with: 'production') fill_in('Name', with: 'production')
click_on 'Save' click_on 'Save'
expect(page).to have_content('production') expect(page).to have_content('production')
end end
it 'developer creates a new environmetn with invalid name' do it 'developer creates a new environment with invalid name' do
within(".environments-section") { click_link 'New environment' } click_link 'New environment'
fill_in('Name', with: 'name,with,commas') fill_in('Name', with: 'name,with,commas')
click_on 'Save' click_on 'Save'
...@@ -458,20 +447,11 @@ RSpec.describe 'Environments page', :js do ...@@ -458,20 +447,11 @@ RSpec.describe 'Environments page', :js do
expect(page).not_to have_content 'review-2' expect(page).not_to have_content 'review-2'
expect(page).to have_content 'staging 2' expect(page).to have_content 'staging 2'
within('.folder-row') do page.click_button _('Expand')
find('.folder-name', text: 'staging').click
end
expect(page).to have_content 'review-1' expect(page).to have_content 'review-1'
expect(page).to have_content 'review-2' expect(page).to have_content 'review-2'
within('.ci-table') do expect(page).to have_content 'Auto stop in'
within('[data-qa-selector="environment_item"]', text: 'review-1') do # rubocop:disable QA/SelectorUsage
expect(find('.js-auto-stop').text).not_to be_empty
end
within('[data-qa-selector="environment_item"]', text: 'review-2') do # rubocop:disable QA/SelectorUsage
expect(find('.js-auto-stop').text).not_to be_empty
end
end
end end
end end
...@@ -494,9 +474,7 @@ RSpec.describe 'Environments page', :js do ...@@ -494,9 +474,7 @@ RSpec.describe 'Environments page', :js do
expect(page).not_to have_content 'review-2' expect(page).not_to have_content 'review-2'
expect(page).to have_content 'staging 2' expect(page).to have_content 'staging 2'
within('.folder-row') do page.click_button _('Expand')
find('.folder-name', text: 'staging').click
end
expect(page).to have_content 'review-1' expect(page).to have_content 'review-1'
expect(page).to have_content 'review-2' expect(page).to have_content 'review-2'
......
...@@ -16,8 +16,6 @@ describe('~/environments/components/new_environments_folder.vue', () => { ...@@ -16,8 +16,6 @@ describe('~/environments/components/new_environments_folder.vue', () => {
let wrapper; let wrapper;
let environmentFolderMock; let environmentFolderMock;
let nestedEnvironment; let nestedEnvironment;
let folderName;
let button;
const findLink = () => wrapper.findByRole('link', { name: s__('Environments|Show all') }); const findLink = () => wrapper.findByRole('link', { name: s__('Environments|Show all') });
...@@ -42,62 +40,93 @@ describe('~/environments/components/new_environments_folder.vue', () => { ...@@ -42,62 +40,93 @@ describe('~/environments/components/new_environments_folder.vue', () => {
environmentFolderMock = jest.fn(); environmentFolderMock = jest.fn();
[nestedEnvironment] = resolvedEnvironmentsApp.environments; [nestedEnvironment] = resolvedEnvironmentsApp.environments;
environmentFolderMock.mockReturnValue(resolvedFolder); environmentFolderMock.mockReturnValue(resolvedFolder);
wrapper = createWrapper({ nestedEnvironment }, createApolloProvider());
await nextTick();
await waitForPromises();
folderName = wrapper.findByText(nestedEnvironment.name);
button = wrapper.findByRole('button', { name: __('Expand') });
}); });
afterEach(() => { afterEach(() => {
wrapper?.destroy(); wrapper?.destroy();
}); });
it('displays the name of the folder', () => { describe('default', () => {
expect(folderName.text()).toBe(nestedEnvironment.name); let folderName;
}); let button;
describe('collapse', () => { beforeEach(async () => {
let icons; wrapper = createWrapper({ nestedEnvironment }, createApolloProvider());
let collapse;
beforeEach(() => { await nextTick();
collapse = wrapper.findComponent(GlCollapse); await waitForPromises();
icons = wrapper.findAllComponents(GlIcon); folderName = wrapper.findByText(nestedEnvironment.name);
button = wrapper.findByRole('button', { name: __('Expand') });
}); });
it('is collapsed by default', () => { it('displays the name of the folder', () => {
const link = findLink(); expect(folderName.text()).toBe(nestedEnvironment.name);
expect(collapse.attributes('visible')).toBeUndefined();
const iconNames = icons.wrappers.map((i) => i.props('name')).slice(0, 2);
expect(iconNames).toEqual(['angle-right', 'folder-o']);
expect(folderName.classes('gl-font-weight-bold')).toBe(false);
expect(link.exists()).toBe(false);
}); });
it('opens on click', async () => { describe('collapse', () => {
await button.trigger('click'); let icons;
let collapse;
const link = findLink();
beforeEach(() => {
expect(button.attributes('aria-label')).toBe(__('Collapse')); collapse = wrapper.findComponent(GlCollapse);
expect(collapse.attributes('visible')).toBe('visible'); icons = wrapper.findAllComponents(GlIcon);
const iconNames = icons.wrappers.map((i) => i.props('name')).slice(0, 2); });
expect(iconNames).toEqual(['angle-down', 'folder-open']);
expect(folderName.classes('gl-font-weight-bold')).toBe(true); it('is collapsed by default', () => {
expect(link.attributes('href')).toBe(nestedEnvironment.latest.folderPath); const link = findLink();
expect(collapse.attributes('visible')).toBeUndefined();
const iconNames = icons.wrappers.map((i) => i.props('name')).slice(0, 2);
expect(iconNames).toEqual(['angle-right', 'folder-o']);
expect(folderName.classes('gl-font-weight-bold')).toBe(false);
expect(link.exists()).toBe(false);
});
it('opens on click', async () => {
await button.trigger('click');
const link = findLink();
expect(button.attributes('aria-label')).toBe(__('Collapse'));
expect(collapse.attributes('visible')).toBe('visible');
const iconNames = icons.wrappers.map((i) => i.props('name')).slice(0, 2);
expect(iconNames).toEqual(['angle-down', 'folder-open']);
expect(folderName.classes('gl-font-weight-bold')).toBe(true);
expect(link.attributes('href')).toBe(nestedEnvironment.latest.folderPath);
});
it('displays all environments when opened', async () => {
await button.trigger('click');
const names = resolvedFolder.environments.map((e) =>
expect.stringMatching(e.nameWithoutType),
);
const environments = wrapper
.findAllComponents(EnvironmentItem)
.wrappers.map((w) => w.text());
expect(environments).toEqual(expect.arrayContaining(names));
});
}); });
});
it('displays all environments when opened', async () => { it.each(['available', 'stopped'])(
await button.trigger('click'); 'with scope=%s, fetches environments with scope',
async (scope) => {
const names = resolvedFolder.environments.map((e) => wrapper = createWrapper({ nestedEnvironment, scope }, createApolloProvider());
expect.stringMatching(e.nameWithoutType),
await nextTick();
await waitForPromises();
expect(environmentFolderMock).toHaveBeenCalledTimes(1);
expect(environmentFolderMock).toHaveBeenCalledWith(
{},
{
environment: nestedEnvironment.latest,
scope,
},
expect.anything(),
expect.anything(),
); );
const environments = wrapper.findAllComponents(EnvironmentItem).wrappers.map((w) => w.text()); },
expect(environments).toEqual(expect.arrayContaining(names)); );
});
});
}); });
...@@ -9,6 +9,7 @@ import { sprintf, __, s__ } from '~/locale'; ...@@ -9,6 +9,7 @@ import { sprintf, __, s__ } from '~/locale';
import EnvironmentsApp from '~/environments/components/new_environments_app.vue'; import EnvironmentsApp from '~/environments/components/new_environments_app.vue';
import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue'; import EnvironmentsFolder from '~/environments/components/new_environment_folder.vue';
import EnvironmentsItem from '~/environments/components/new_environment_item.vue'; import EnvironmentsItem from '~/environments/components/new_environment_item.vue';
import EmptyState from '~/environments/components/empty_state.vue';
import StopEnvironmentModal from '~/environments/components/stop_environment_modal.vue'; import StopEnvironmentModal from '~/environments/components/stop_environment_modal.vue';
import CanaryUpdateModal from '~/environments/components/canary_update_modal.vue'; import CanaryUpdateModal from '~/environments/components/canary_update_modal.vue';
import { resolvedEnvironmentsApp, resolvedFolder, resolvedEnvironment } from './graphql/mock_data'; import { resolvedEnvironmentsApp, resolvedFolder, resolvedEnvironment } from './graphql/mock_data';
...@@ -121,6 +122,14 @@ describe('~/environments/components/new_environments_app.vue', () => { ...@@ -121,6 +122,14 @@ describe('~/environments/components/new_environments_app.vue', () => {
expect(text).toContainEqual(expect.stringMatching('production')); expect(text).toContainEqual(expect.stringMatching('production'));
}); });
it('should show an empty state with no environments', async () => {
await createWrapperWithMocked({
environmentsApp: { ...resolvedEnvironmentsApp, environments: [] },
});
expect(wrapper.findComponent(EmptyState).exists()).toBe(true);
});
it('should show a button to create a new environment', async () => { it('should show a button to create a new environment', async () => {
await createWrapperWithMocked({ await createWrapperWithMocked({
environmentsApp: resolvedEnvironmentsApp, environmentsApp: resolvedEnvironmentsApp,
......
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