Commit 27704ed5 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 841df318 0aecfcaa
<script>
/*
* The commented part of this component needs to be re-enabled in the refactor process,
* See here for more info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64939
*/
import {
GlBadge,
GlButton,
GlModal,
GlModalDirective,
GlTooltipDirective,
GlEmptyState,
GlTab,
GlTabs,
GlSprintf,
} from '@gitlab/ui';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { objectToQuery } from '~/lib/utils/url_utility';
import { s__, __ } from '~/locale';
// import AdditionalMetadata from '~/packages/details/components/additional_metadata.vue';
// import DependencyRow from '~/packages/details/components/dependency_row.vue';
// import InstallationCommands from '~/packages/details/components/installation_commands.vue';
// import PackageFiles from '~/packages/details/components/package_files.vue';
// import PackageHistory from '~/packages/details/components/package_history.vue';
// import PackageListRow from '~/packages/shared/components/package_list_row.vue';
import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue';
import {
PackageType,
TrackingActions,
SHOW_DELETE_SUCCESS_ALERT,
} from '~/packages/shared/constants';
import { packageTypeToTrackCategory } from '~/packages/shared/utils';
import Tracking from '~/tracking';
export default {
name: 'PackagesApp',
components: {
GlBadge,
GlButton,
GlEmptyState,
GlModal,
GlTab,
GlTabs,
GlSprintf,
PackageTitle: () => import('~/packages/details/components/package_title.vue'),
TerraformTitle: () =>
import('~/packages_and_registries/infrastructure_registry/components/details_title.vue'),
PackagesListLoader,
// PackageListRow,
// DependencyRow,
// PackageHistory,
// AdditionalMetadata,
// InstallationCommands,
// PackageFiles,
},
directives: {
GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective,
},
mixins: [Tracking.mixin()],
inject: [
'titleComponent',
'projectName',
'canDelete',
'svgPath',
'npmPath',
'npmHelpPath',
'projectListUrl',
'groupListUrl',
],
trackingActions: { ...TrackingActions },
data() {
return {
fileToDelete: null,
packageEntity: {},
};
},
computed: {
packageFiles() {
return this.packageEntity.packageFiles;
},
isLoading() {
return false;
},
isValidPackage() {
return Boolean(this.packageEntity.name);
},
tracking() {
return {
category: packageTypeToTrackCategory(this.packageEntity.package_type),
};
},
hasVersions() {
return this.packageEntity.versions?.length > 0;
},
packageDependencies() {
return this.packageEntity.dependency_links || [];
},
showDependencies() {
return this.packageEntity.package_type === PackageType.NUGET;
},
showFiles() {
return this.packageEntity?.package_type !== PackageType.COMPOSER;
},
},
methods: {
formatSize(size) {
return numberToHumanSize(size);
},
getPackageVersions() {
if (!this.packageEntity.versions) {
// this.fetchPackageVersions();
}
},
async confirmPackageDeletion() {
this.track(TrackingActions.DELETE_PACKAGE);
await this.deletePackage();
const returnTo =
!this.groupListUrl || document.referrer.includes(this.projectName)
? this.projectListUrl
: this.groupListUrl; // to avoid security issue url are supplied from backend
const modalQuery = objectToQuery({ [SHOW_DELETE_SUCCESS_ALERT]: true });
window.location.replace(`${returnTo}?${modalQuery}`);
},
handleFileDelete(file) {
this.track(TrackingActions.REQUEST_DELETE_PACKAGE_FILE);
this.fileToDelete = { ...file };
this.$refs.deleteFileModal.show();
},
confirmFileDelete() {
this.track(TrackingActions.DELETE_PACKAGE_FILE);
// this.deletePackageFile(this.fileToDelete.id);
this.fileToDelete = null;
},
},
i18n: {
deleteModalTitle: s__(`PackageRegistry|Delete Package Version`),
deleteModalContent: s__(
`PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?`,
),
deleteFileModalTitle: s__(`PackageRegistry|Delete Package File`),
deleteFileModalContent: s__(
`PackageRegistry|You are about to delete %{filename}. This is a destructive action that may render your package unusable. Are you sure?`,
),
},
modal: {
packageDeletePrimaryAction: {
text: __('Delete'),
attributes: [
{ variant: 'danger' },
{ category: 'primary' },
{ 'data-qa-selector': 'delete_modal_button' },
],
},
fileDeletePrimaryAction: {
text: __('Delete'),
attributes: [{ variant: 'danger' }, { category: 'primary' }],
},
cancelAction: {
text: __('Cancel'),
},
},
};
</script>
<template>
<gl-empty-state
v-if="!isValidPackage"
:title="s__('PackageRegistry|Unable to load package')"
:description="s__('PackageRegistry|There was a problem fetching the details for this package.')"
:svg-path="svgPath"
/>
<div v-else class="packages-app">
<component :is="titleComponent">
<template #delete-button>
<gl-button
v-if="canDelete"
v-gl-modal="'delete-modal'"
class="js-delete-button"
variant="danger"
category="primary"
data-qa-selector="delete_button"
>
{{ __('Delete') }}
</gl-button>
</template>
</component>
<gl-tabs>
<gl-tab :title="__('Detail')">
<div data-qa-selector="package_information_content">
<!-- <package-history :package-entity="packageEntity" :project-name="projectName" />
<installation-commands
:package-entity="packageEntity"
:npm-path="npmPath"
:npm-help-path="npmHelpPath"
/>
<additional-metadata :package-entity="packageEntity" /> -->
</div>
<!-- <package-files
v-if="showFiles"
:package-files="packageFiles"
:can-delete="canDelete"
@download-file="track($options.trackingActions.PULL_PACKAGE)"
@delete-file="handleFileDelete"
/> -->
</gl-tab>
<gl-tab v-if="showDependencies" title-item-class="js-dependencies-tab">
<template #title>
<span>{{ __('Dependencies') }}</span>
<gl-badge size="sm" data-testid="dependencies-badge">{{
packageDependencies.length
}}</gl-badge>
</template>
<template v-if="packageDependencies.length > 0">
<dependency-row
v-for="(dep, index) in packageDependencies"
:key="index"
:dependency="dep"
/>
</template>
<p v-else class="gl-mt-3" data-testid="no-dependencies-message">
{{ s__('PackageRegistry|This NuGet package has no dependencies.') }}
</p>
</gl-tab>
<gl-tab
:title="__('Other versions')"
title-item-class="js-versions-tab"
@click="getPackageVersions"
>
<template v-if="isLoading && !hasVersions">
<packages-list-loader />
</template>
<template v-else-if="hasVersions">
<!-- <package-list-row
v-for="v in packageEntity.versions"
:key="v.id"
:package-entity="{ name: packageEntity.name, ...v }"
:package-link="v.id.toString()"
:disable-delete="true"
:show-package-type="false"
/> -->
</template>
<p v-else class="gl-mt-3" data-testid="no-versions-message">
{{ s__('PackageRegistry|There are no other versions of this package.') }}
</p>
</gl-tab>
</gl-tabs>
<gl-modal
ref="deleteModal"
class="js-delete-modal"
modal-id="delete-modal"
:action-primary="$options.modal.packageDeletePrimaryAction"
:action-cancel="$options.modal.cancelAction"
@primary="confirmPackageDeletion"
@canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE)"
>
<template #modal-title>{{ $options.i18n.deleteModalTitle }}</template>
<gl-sprintf :message="$options.i18n.deleteModalContent">
<template #version>
<strong>{{ packageEntity.version }}</strong>
</template>
<template #name>
<strong>{{ packageEntity.name }}</strong>
</template>
</gl-sprintf>
</gl-modal>
<gl-modal
ref="deleteFileModal"
modal-id="delete-file-modal"
:action-primary="$options.modal.fileDeletePrimaryAction"
:action-cancel="$options.modal.cancelAction"
@primary="confirmFileDelete"
@canceled="track($options.trackingActions.CANCEL_DELETE_PACKAGE_FILE)"
>
<template #modal-title>{{ $options.i18n.deleteFileModalTitle }}</template>
<gl-sprintf v-if="fileToDelete" :message="$options.i18n.deleteFileModalContent">
<template #filename>
<strong>{{ fileToDelete.file_name }}</strong>
</template>
</gl-sprintf>
</gl-modal>
</div>
</template>
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import Translate from '~/vue_shared/translate';
import PackagesApp from '../components/details/app.vue';
Vue.use(Translate);
export default () => {
const el = document.getElementById('js-vue-packages-detail-new');
if (!el) {
return null;
}
const { canDelete, ...datasetOptions } = el.dataset;
return new Vue({
el,
provide: {
canDelete: parseBoolean(canDelete),
titleComponent: 'PackageTitle',
...datasetOptions,
},
render(createElement) {
return createElement(PackagesApp);
},
});
};
import initPackageDetail from '~/packages/details/';
initPackageDetail();
(async function initPackage() {
let app;
if (document.getElementById('js-vue-packages-detail-new')) {
app = await import(
/* webpackChunkName: 'new_package_app' */ `~/packages_and_registries/package_registry/pages/details.js`
);
} else {
app = await import('~/packages/details/');
}
app.default();
})();
......@@ -63,4 +63,29 @@ module PackagesHelper
project.container_expiration_policy.nil? &&
project.container_repositories.exists?
end
def package_details_data(project, package = nil)
{
package: package ? package_from_presenter(package) : nil,
can_delete: can?(current_user, :destroy_package, project).to_s,
svg_path: image_path('illustrations/no-packages.svg'),
npm_path: package_registry_instance_url(:npm),
npm_help_path: help_page_path('user/packages/npm_registry/index'),
maven_path: package_registry_project_url(project.id, :maven),
maven_help_path: help_page_path('user/packages/maven_repository/index'),
conan_path: package_registry_project_url(project.id, :conan),
conan_help_path: help_page_path('user/packages/conan_repository/index'),
nuget_path: nuget_package_registry_url(project.id),
nuget_help_path: help_page_path('user/packages/nuget_repository/index'),
pypi_path: pypi_registry_url(project.id),
pypi_setup_path: package_registry_project_url(project.id, :pypi),
pypi_help_path: help_page_path('user/packages/pypi_repository/index'),
composer_path: composer_registry_url(project&.group&.id),
composer_help_path: help_page_path('user/packages/composer_repository/index'),
project_name: project.name,
project_list_url: project_packages_path(project),
group_list_url: project.group ? group_packages_path(project.group) : '',
composer_config_repository_name: composer_config_repository_name(project.group&.id)
}
end
end
......@@ -40,7 +40,6 @@ class WikiPage
end
validates :title, presence: true
validates :content, presence: true
validate :validate_path_limits, if: :title_changed?
validate :validate_content_size_limit, if: :content_changed?
......
......@@ -6,23 +6,7 @@
.row
.col-12
#js-vue-packages-detail{ data: { package: package_from_presenter(@package),
can_delete: can?(current_user, :destroy_package, @project).to_s,
svg_path: image_path('illustrations/no-packages.svg'),
npm_path: package_registry_instance_url(:npm),
npm_help_path: help_page_path('user/packages/npm_registry/index'),
maven_path: package_registry_project_url(@project.id, :maven),
maven_help_path: help_page_path('user/packages/maven_repository/index'),
conan_path: package_registry_project_url(@project.id, :conan),
conan_help_path: help_page_path('user/packages/conan_repository/index'),
nuget_path: nuget_package_registry_url(@project.id),
nuget_help_path: help_page_path('user/packages/nuget_repository/index'),
pypi_path: pypi_registry_url(@project.id),
pypi_setup_path: package_registry_project_url(@project.id, :pypi),
pypi_help_path: help_page_path('user/packages/pypi_repository/index'),
composer_path: composer_registry_url(@project&.group&.id),
composer_help_path: help_page_path('user/packages/composer_repository/index'),
project_name: @project.name,
project_list_url: project_packages_path(@project),
group_list_url: @project.group ? group_packages_path(@project.group) : '',
composer_config_repository_name: composer_config_repository_name(@project.group&.id)} }
- if Feature.enabled?(:package_details_apollo)
#js-vue-packages-detail-new{ data: package_details_data(@project) }
- else
#js-vue-packages-detail{ data: package_details_data(@project, @package) }
......@@ -84,7 +84,6 @@
- mlops
- mobile_signing_deployment
- navigation
- not_owned
- omnibus_package
- on_call_schedule_management
- onboarding
......
---
name: package_details_apollo
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64939
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334786
milestone: '14.1'
type: development
group: group::package
default_enabled: false
......@@ -226,7 +226,7 @@ Gitaly Cluster consists of multiple components:
- [Load balancer](praefect.md#load-balancer) for distributing requests and providing fault-tolerant access to
Praefect nodes.
- [Praefect](praefect.md#praefect) nodes for managing the cluster and routing requests to Gitaly nodes.
- [PostgreSQL database](praefect.md#postgresql) for persisting cluster metadata and [PgBouncer](praefect.md#pgbouncer),
- [PostgreSQL database](praefect.md#postgresql) for persisting cluster metadata and [PgBouncer](praefect.md#use-pgbouncer),
recommended for pooling Praefect's database connections.
- Gitaly nodes to provide repository storage and Git access.
......
This diff is collapsed.
......@@ -52,6 +52,20 @@ This content has been moved to a [new location](replication_and_failover.md#conf
}
```
You can pass additional configuration parameters per database, for example:
```ruby
pgbouncer['databases'] = {
gitlabhq_production: {
...
pool_mode: 'transaction'
}
}
```
Use these parameters with caution. For the complete list of parameters refer to the
[PgBouncer documentation](https://www.pgbouncer.org/config.html#section-databases).
1. Run `gitlab-ctl reconfigure`
1. On the node running Puma, make sure the following is set in `/etc/gitlab/gitlab.rb`
......
......@@ -415,6 +415,10 @@ which is used to track vulnerabilities
as new commits are pushed to the repository.
The attributes used to generate the location fingerprint also depend on the type of scanning.
### Details
The `details` field is an object that supports many different content elements that are displayed when viewing vulnerability information. An example of the various data elements can be seen in the [security-reports repository](https://gitlab.com/gitlab-examples/security/security-reports/-/tree/master/samples/details-example).
#### Dependency Scanning
The `location` of a Dependency Scanning vulnerability is composed of a `dependency` and a `file`.
......
......@@ -4,8 +4,12 @@ module Gitlab
module Pagination
module Keyset
class Iterator
def initialize(scope:, use_union_optimization: false)
@scope = scope
UnsupportedScopeOrder = Class.new(StandardError)
def initialize(scope:, use_union_optimization: true)
@scope, success = Gitlab::Pagination::Keyset::SimpleOrderBuilder.build(scope)
raise(UnsupportedScopeOrder, 'The order on the scope does not support keyset pagination') unless success
@order = Gitlab::Pagination::Keyset::Order.extract_keyset_order_object(scope)
@use_union_optimization = use_union_optimization
end
......
......@@ -23,7 +23,8 @@ module QA
end
def add_to_file_content(content)
text_area.set content
text_area.click
text_area.send_keys(:home, content) # starts in the beginning of the line
text_area.has_text?(content) # wait for changes to take effect
end
......
......@@ -44,7 +44,13 @@ RSpec.describe 'Group Packages' do
it_behaves_like 'packages list', check_project_name: true
it_behaves_like 'package details link'
context 'when package_details_apollo feature flag is off' do
before do
stub_feature_flags(package_details_apollo: false)
end
it_behaves_like 'package details link'
end
it 'allows you to navigate to the project page' do
find('[data-testid="root-link"]', text: project.name).click
......
......@@ -23,12 +23,18 @@ RSpec.describe 'PackageFiles' do
expect(status_code).to eq(200)
end
it 'renders the download link with the correct url', :js do
visit project_package_path(project, package)
context 'when package_details_apollo feature flag is off' do
before do
stub_feature_flags(package_details_apollo: false)
end
download_url = download_project_package_file_path(project, package_file)
it 'renders the download link with the correct url', :js do
visit project_package_path(project, package)
expect(page).to have_link(package_file.file_name, href: download_url)
download_url = download_project_package_file_path(project, package_file)
expect(page).to have_link(package_file.file_name, href: download_url)
end
end
it 'does not allow download of package belonging to different project' do
......
......@@ -37,7 +37,13 @@ RSpec.describe 'Packages' do
it_behaves_like 'packages list'
it_behaves_like 'package details link'
context 'when package_details_apollo feature flag is off' do
before do
stub_feature_flags(package_details_apollo: false)
end
it_behaves_like 'package details link'
end
context 'deleting a package' do
let_it_be(:project) { create(:project) }
......
import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PackagesApp from '~/packages_and_registries/package_registry/components/details/app.vue';
describe('PackagesApp', () => {
let wrapper;
function createComponent() {
wrapper = shallowMount(PackagesApp, {
provide: {
titleComponent: 'titleComponent',
projectName: 'projectName',
canDelete: 'canDelete',
svgPath: 'svgPath',
npmPath: 'npmPath',
npmHelpPath: 'npmHelpPath',
projectListUrl: 'projectListUrl',
groupListUrl: 'groupListUrl',
},
});
}
const emptyState = () => wrapper.findComponent(GlEmptyState);
afterEach(() => {
wrapper.destroy();
});
it('renders an empty state component', () => {
createComponent();
expect(emptyState().exists()).toBe(true);
});
});
......@@ -18,110 +18,127 @@ RSpec.describe Gitlab::Pagination::Keyset::Iterator do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: column,
column_expression: klass.arel_table[column],
order_expression: ::Gitlab::Database.nulls_order(column, direction, nulls_position),
reversed_order_expression: ::Gitlab::Database.nulls_order(column, reverse_direction, reverse_nulls_position),
order_direction: direction,
nullable: nulls_position,
distinct: false
column_expression: klass.arel_table[column],
order_expression: ::Gitlab::Database.nulls_order(column, direction, nulls_position),
reversed_order_expression: ::Gitlab::Database.nulls_order(column, reverse_direction, reverse_nulls_position),
order_direction: direction,
nullable: nulls_position,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: klass.arel_table[:id].send(direction),
add_to_projections: true
order_expression: klass.arel_table[:id].send(direction)
)
])
end
let(:scope) { project.issues.reorder(custom_reorder) }
subject { described_class.new(scope: scope) }
shared_examples 'iterator examples' do
describe '.each_batch' do
it 'yields an ActiveRecord::Relation when a block is given' do
iterator.each_batch(of: 1) do |relation|
expect(relation).to be_a_kind_of(ActiveRecord::Relation)
end
end
describe '.each_batch' do
it 'yields an ActiveRecord::Relation when a block is given' do
subject.each_batch(of: 1) do |relation|
expect(relation).to be_a_kind_of(ActiveRecord::Relation)
it 'raises error when ordering configuration cannot be automatically determined' do
expect do
described_class.new(scope: MergeRequestDiffCommit.order(:merge_request_diff_id, :relative_order))
end.to raise_error /The order on the scope does not support keyset pagination/
end
end
it 'accepts a custom batch size' do
count = 0
it 'accepts a custom batch size' do
count = 0
subject.each_batch(of: 2) { |relation| count += relation.count(:all) }
iterator.each_batch(of: 2) { |relation| count += relation.count(:all) }
expect(count).to eq(9)
end
expect(count).to eq(9)
end
it 'allows updating of the yielded relations' do
time = Time.current
it 'allows updating of the yielded relations' do
time = Time.current
subject.each_batch(of: 2) do |relation|
relation.update_all(updated_at: time)
end
iterator.each_batch(of: 2) do |relation|
Issue.connection.execute("UPDATE issues SET updated_at = '#{time.to_s(:inspect)}' WHERE id IN (#{relation.reselect(:id).to_sql})")
end
expect(Issue.where(updated_at: time).count).to eq(9)
end
expect(Issue.pluck(:updated_at)).to all(be_within(5.seconds).of(time))
end
context 'with ordering direction' do
context 'when ordering asc' do
it 'orders ascending by default, including secondary order column' do
positions = []
context 'with ordering direction' do
context 'when ordering asc' do
it 'orders ascending by default, including secondary order column' do
positions = []
subject.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
iterator.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
expect(positions).to eq(project.issues.order_relative_position_asc.order(id: :asc).pluck(:relative_position, :id))
expect(positions).to eq(project.issues.order_relative_position_asc.order(id: :asc).pluck(:relative_position, :id))
end
end
end
context 'when reversing asc order' do
let(:scope) { project.issues.order(custom_reorder.reversed_order) }
context 'when reversing asc order' do
let(:scope) { project.issues.order(custom_reorder.reversed_order) }
it 'orders in reverse of ascending' do
positions = []
it 'orders in reverse of ascending' do
positions = []
subject.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
iterator.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
expect(positions).to eq(project.issues.order_relative_position_desc.order(id: :desc).pluck(:relative_position, :id))
expect(positions).to eq(project.issues.order_relative_position_desc.order(id: :desc).pluck(:relative_position, :id))
end
end
end
context 'when asc order, with nulls first' do
let(:nulls_position) { :nulls_first }
context 'when asc order, with nulls first' do
let(:nulls_position) { :nulls_first }
it 'orders ascending with nulls first' do
positions = []
it 'orders ascending with nulls first' do
positions = []
subject.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
iterator.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
expect(positions).to eq(project.issues.reorder(::Gitlab::Database.nulls_first_order('relative_position', 'ASC')).order(id: :asc).pluck(:relative_position, :id))
expect(positions).to eq(project.issues.reorder(::Gitlab::Database.nulls_first_order('relative_position', 'ASC')).order(id: :asc).pluck(:relative_position, :id))
end
end
end
context 'when ordering desc' do
let(:direction) { :desc }
let(:nulls_position) { :nulls_last }
context 'when ordering desc' do
let(:direction) { :desc }
let(:nulls_position) { :nulls_last }
it 'orders descending' do
positions = []
it 'orders descending' do
positions = []
subject.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
iterator.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:relative_position, :id)) }
expect(positions).to eq(project.issues.reorder(::Gitlab::Database.nulls_last_order('relative_position', 'DESC')).order(id: :desc).pluck(:relative_position, :id))
expect(positions).to eq(project.issues.reorder(::Gitlab::Database.nulls_last_order('relative_position', 'DESC')).order(id: :desc).pluck(:relative_position, :id))
end
end
end
context 'when ordering by columns are repeated twice' do
let(:direction) { :desc }
let(:column) { :id }
context 'when ordering by columns are repeated twice' do
let(:direction) { :desc }
let(:column) { :id }
it 'orders descending' do
positions = []
it 'orders descending' do
positions = []
subject.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:id)) }
iterator.each_batch(of: 2) { |rel| positions.concat(rel.pluck(:id)) }
expect(positions).to eq(project.issues.reorder(id: :desc).pluck(:id))
expect(positions).to eq(project.issues.reorder(id: :desc).pluck(:id))
end
end
end
end
end
context 'when use_union_optimization is used' do
subject(:iterator) { described_class.new(scope: scope, use_union_optimization: true) }
include_examples 'iterator examples'
end
context 'when use_union_optimization is not used' do
subject(:iterator) { described_class.new(scope: scope, use_union_optimization: false) }
include_examples 'iterator examples'
end
end
......@@ -201,11 +201,10 @@ RSpec.describe WikiPage do
expect(subject.errors.messages).to eq(title: ["can't be blank"])
end
it "validates presence of content" do
it "does not validate presence of content" do
subject.attributes.delete(:content)
expect(subject).not_to be_valid
expect(subject.errors.messages).to eq(content: ["can't be blank"])
expect(subject).to be_valid
end
describe '#validate_content_size_limit' do
......
......@@ -20,17 +20,6 @@ RSpec.shared_examples 'User creates wiki page' do
click_link "Create your first page"
end
it "shows validation error message if the form is force submitted", :js do
page.within(".wiki-form") do
fill_in(:wiki_content, with: "")
page.execute_script("document.querySelector('.wiki-form').submit()")
page.accept_alert # manually force form submit
end
expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank")
end
it "disables the submit button", :js do
page.within(".wiki-form") do
fill_in(:wiki_content, with: "")
......
......@@ -90,19 +90,6 @@ RSpec.shared_examples 'User updates wiki page' do
expect(page).to have_field('wiki[message]', with: 'Update Wiki title')
end
it 'shows a validation error message if the form is force submitted', :js do
fill_in(:wiki_content, with: '')
page.execute_script("document.querySelector('.wiki-form').submit()")
page.accept_alert # manually force form submit
expect(page).to have_selector('.wiki-form')
expect(page).to have_content('Edit Page')
expect(page).to have_content('The form contains the following error:')
expect(page).to have_content("Content can't be blank")
expect(find('textarea#wiki_content').value).to eq('')
end
it "disables the submit button", :js do
page.within(".wiki-form") do
fill_in(:wiki_content, with: "")
......
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