From 57060295031f1ad2d5f058889561a68ede04d2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Mon, 22 Jan 2018 19:31:03 +0100 Subject: Expose auto_devops_domain in admin settings view --- spec/features/admin/admin_settings_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'spec/features') diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 1218ea52227..cfc6697c79a 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -47,6 +47,16 @@ feature 'Admin updates settings' do expect(page).to have_content "Application settings saved successfully" end + scenario 'Change AutoDevOps settings' do + check 'Enabled Auto DevOps (Beta) for projects by default' + fill_in 'Auto devops domain', with: 'domain.com' + click_button 'Save' + + expect(current_application_settings.auto_devops_enabled?).to be true + expect(current_application_settings.auto_devops_domain).to eq('domain.com') + expect(page).to have_content "Application settings saved successfully" + end + scenario 'Change Slack Notifications Service template settings' do first(:link, 'Service Templates').click click_link 'Slack notifications' -- cgit v1.2.1 From 56bcb3e8c87bf25cb33ed02832c146e966cc4d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Sat, 3 Feb 2018 01:56:11 +0100 Subject: Use Gitlab::CurrentSettings instead of current_application_settings --- spec/features/admin/admin_settings_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec/features') diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 3da2f4e2fdb..39b213988f0 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -52,8 +52,8 @@ feature 'Admin updates settings' do fill_in 'Auto devops domain', with: 'domain.com' click_button 'Save' - expect(current_application_settings.auto_devops_enabled?).to be true - expect(current_application_settings.auto_devops_domain).to eq('domain.com') + expect(Gitlab::CurrentSettings.auto_devops_enabled?).to be true + expect(Gitlab::CurrentSettings.auto_devops_domain).to eq('domain.com') expect(page).to have_content "Application settings saved successfully" end -- cgit v1.2.1 From 3be32027b6c543287b94b5be34bf53039d86f88c Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Tue, 16 Jan 2018 15:13:14 -0600 Subject: Use dynamic variable list in scheduled pipelines and group/project CI secret variables See https://gitlab.com/gitlab-org/gitlab-ce/issues/39118 Conflicts: app/views/ci/variables/_form.html.haml app/views/ci/variables/_table.html.haml ee/app/views/ci/variables/_environment_scope.html.haml spec/javascripts/ci_variable_list/ci_variable_list_ee_spec.js spec/javascripts/fixtures/projects.rb --- spec/features/group_variables_spec.rb | 69 +-------------- spec/features/project_variables_spec.rb | 18 ++++ spec/features/variables_spec.rb | 145 -------------------------------- 3 files changed, 22 insertions(+), 210 deletions(-) create mode 100644 spec/features/project_variables_spec.rb delete mode 100644 spec/features/variables_spec.rb (limited to 'spec/features') diff --git a/spec/features/group_variables_spec.rb b/spec/features/group_variables_spec.rb index e9b375f4c94..f7863807572 100644 --- a/spec/features/group_variables_spec.rb +++ b/spec/features/group_variables_spec.rb @@ -3,76 +3,15 @@ require 'spec_helper' feature 'Group variables', :js do let(:user) { create(:user) } let(:group) { create(:group) } + let!(:variable) { create(:ci_group_variable, key: 'test_key', value: 'test value', group: group) } + let(:page_path) { group_settings_ci_cd_path(group) } background do group.add_master(user) gitlab_sign_in(user) - end - - context 'when user creates a new variable' do - background do - visit group_settings_ci_cd_path(group) - fill_in 'variable_key', with: 'AAA' - fill_in 'variable_value', with: 'AAA123' - find(:css, "#variable_protected").set(true) - click_on 'Add new variable' - end - - scenario 'user sees the created variable' do - page.within('.variables-table') do - expect(find(".variable-key")).to have_content('AAA') - expect(find(".variable-value")).to have_content('******') - expect(find(".variable-protected")).to have_content('Yes') - end - click_on 'Reveal value' - page.within('.variables-table') do - expect(find(".variable-value")).to have_content('AAA123') - end - end - end - - context 'when user edits a variable' do - background do - create(:ci_group_variable, key: 'AAA', value: 'AAA123', protected: true, - group: group) - - visit group_settings_ci_cd_path(group) - page.within('.variable-menu') do - click_on 'Update' - end - - fill_in 'variable_key', with: 'BBB' - fill_in 'variable_value', with: 'BBB123' - find(:css, "#variable_protected").set(false) - click_on 'Save variable' - end - - scenario 'user sees the updated variable' do - page.within('.variables-table') do - expect(find(".variable-key")).to have_content('BBB') - expect(find(".variable-value")).to have_content('******') - expect(find(".variable-protected")).to have_content('No') - end - end + visit page_path end - context 'when user deletes a variable' do - background do - create(:ci_group_variable, key: 'BBB', value: 'BBB123', protected: false, - group: group) - - visit group_settings_ci_cd_path(group) - - page.within('.variable-menu') do - page.accept_alert 'Are you sure?' do - click_on 'Remove' - end - end - end - - scenario 'user does not see the deleted variable' do - expect(page).to have_no_css('.variables-table') - end - end + it_behaves_like 'variable list' end diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb new file mode 100644 index 00000000000..0ba2224359a --- /dev/null +++ b/spec/features/project_variables_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe 'Project variables', :js do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:variable) { create(:ci_variable, key: 'test_key', value: 'test value') } + let(:page_path) { project_settings_ci_cd_path(project) } + + before do + sign_in(user) + project.add_master(user) + project.variables << variable + + visit page_path + end + + it_behaves_like 'variable list' +end diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb deleted file mode 100644 index 79ca2b4bb4a..00000000000 --- a/spec/features/variables_spec.rb +++ /dev/null @@ -1,145 +0,0 @@ -require 'spec_helper' - -describe 'Project variables', :js do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:variable) { create(:ci_variable, key: 'test_key', value: 'test value') } - - before do - sign_in(user) - project.add_master(user) - project.variables << variable - - visit project_settings_ci_cd_path(project) - end - - it 'shows list of variables' do - page.within('.variables-table') do - expect(page).to have_content(variable.key) - end - end - - it 'adds new secret variable' do - fill_in('variable_key', with: 'key') - fill_in('variable_value', with: 'key value') - click_button('Add new variable') - - expect(page).to have_content('Variable was successfully created.') - page.within('.variables-table') do - expect(page).to have_content('key') - expect(page).to have_content('No') - end - end - - it 'adds empty variable' do - fill_in('variable_key', with: 'new_key') - fill_in('variable_value', with: '') - click_button('Add new variable') - - expect(page).to have_content('Variable was successfully created.') - page.within('.variables-table') do - expect(page).to have_content('new_key') - end - end - - it 'adds new protected variable' do - fill_in('variable_key', with: 'key') - fill_in('variable_value', with: 'value') - check('Protected') - click_button('Add new variable') - - expect(page).to have_content('Variable was successfully created.') - page.within('.variables-table') do - expect(page).to have_content('key') - expect(page).to have_content('Yes') - end - end - - it 'reveals and hides new variable' do - fill_in('variable_key', with: 'key') - fill_in('variable_value', with: 'key value') - click_button('Add new variable') - - page.within('.variables-table') do - expect(page).to have_content('key') - expect(page).to have_content('******') - end - - click_button('Reveal values') - - page.within('.variables-table') do - expect(page).to have_content('key') - expect(page).to have_content('key value') - end - - click_button('Hide values') - - page.within('.variables-table') do - expect(page).to have_content('key') - expect(page).to have_content('******') - end - end - - it 'deletes variable' do - page.within('.variables-table') do - accept_confirm { click_on 'Remove' } - end - - expect(page).not_to have_selector('variables-table') - end - - it 'edits variable' do - page.within('.variables-table') do - click_on 'Update' - end - - expect(page).to have_content('Update variable') - fill_in('variable_key', with: 'key') - fill_in('variable_value', with: 'key value') - click_button('Save variable') - - expect(page).to have_content('Variable was successfully updated.') - expect(project.variables(true).first.value).to eq('key value') - end - - it 'edits variable with empty value' do - page.within('.variables-table') do - click_on 'Update' - end - - expect(page).to have_content('Update variable') - fill_in('variable_value', with: '') - click_button('Save variable') - - expect(page).to have_content('Variable was successfully updated.') - expect(project.variables(true).first.value).to eq('') - end - - it 'edits variable to be protected' do - page.within('.variables-table') do - click_on 'Update' - end - - expect(page).to have_content('Update variable') - check('Protected') - click_button('Save variable') - - expect(page).to have_content('Variable was successfully updated.') - expect(project.variables(true).first).to be_protected - end - - it 'edits variable to be unprotected' do - project.variables.first.update(protected: true) - - page.within('.variables-table') do - click_on 'Update' - end - - expect(page).to have_content('Update variable') - uncheck('Protected') - click_button('Save variable') - - expect(page).to have_content('Variable was successfully updated.') - expect(project.variables(true).first).not_to be_protected - end -end -- cgit v1.2.1 From 6b0c6e69e1b249f14624550fcbca7f5ee6f51410 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Fri, 1 Dec 2017 13:58:49 +0000 Subject: Use hashed storage in the specs --- spec/features/projects/import_export/namespace_export_file_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'spec/features') diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb index c1fccf4a40b..7d056b0c140 100644 --- a/spec/features/projects/import_export/namespace_export_file_spec.rb +++ b/spec/features/projects/import_export/namespace_export_file_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Import/Export - Namespace export file cleanup', :js do +describe 'Import/Export - Namespace export file cleanup', :js do let(:export_path) { Dir.mktmpdir('namespace_export_file_spec') } before do @@ -42,13 +42,13 @@ feature 'Import/Export - Namespace export file cleanup', :js do end describe 'legacy storage' do - let(:project) { create(:project) } + let(:project) { create(:project, :legacy_storage) } it_behaves_like 'handling project exports on namespace change' end describe 'hashed storage' do - let(:project) { create(:project, :hashed) } + let(:project) { create(:project) } it_behaves_like 'handling project exports on namespace change' end -- cgit v1.2.1 From d40912bb4944d0ac9adae45f7c0621f40b996406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Javier=20L=C3=B3pez?= Date: Wed, 7 Feb 2018 16:33:12 +0000 Subject: Removing gitaly flags --- .../projects/wiki/user_updates_wiki_page_spec.rb | 304 +++++++++++---------- .../projects/wiki/user_views_wiki_page_spec.rb | 205 +++++++------- 2 files changed, 263 insertions(+), 246 deletions(-) (limited to 'spec/features') diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb index 4d2a08afecc..ef1bb712846 100644 --- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb @@ -1,226 +1,234 @@ require 'spec_helper' -# Remove skip_gitaly_mock flag when gitaly_update_page implements moving pages -describe 'User updates wiki page', :skip_gitaly_mock do - let(:user) { create(:user) } - - before do - project.add_master(user) - sign_in(user) - end - - context 'when wiki is empty' do +describe 'User updates wiki page' do + shared_examples 'wiki page user update' do + let(:user) { create(:user) } before do - visit(project_wikis_path(project)) + project.add_master(user) + sign_in(user) end - context 'in a user namespace' do - let(:project) { create(:project, namespace: user.namespace) } - - it 'redirects back to the home edit page' do - page.within(:css, '.wiki-form .form-actions') do - click_on('Cancel') - end - - expect(current_path).to eq project_wiki_path(project, :home) + context 'when wiki is empty' do + before do + visit(project_wikis_path(project)) end - it 'updates a page that has a path', :js do - click_on('New page') + context 'in a user namespace' do + let(:project) { create(:project, namespace: user.namespace) } - page.within('#modal-new-wiki') do - fill_in(:new_wiki_path, with: 'one/two/three-test') - click_on('Create page') - end + it 'redirects back to the home edit page' do + page.within(:css, '.wiki-form .form-actions') do + click_on('Cancel') + end - page.within '.wiki-form' do - fill_in(:wiki_content, with: 'wiki content') - click_on('Create page') + expect(current_path).to eq project_wiki_path(project, :home) end - expect(current_path).to include('one/two/three-test') - expect(find('.wiki-pages')).to have_content('Three') + it 'updates a page that has a path', :js do + click_on('New page') - first(:link, text: 'Three').click + page.within('#modal-new-wiki') do + fill_in(:new_wiki_path, with: 'one/two/three-test') + click_on('Create page') + end - expect(find('.nav-text')).to have_content('Three') + page.within '.wiki-form' do + fill_in(:wiki_content, with: 'wiki content') + click_on('Create page') + end - click_on('Edit') + expect(current_path).to include('one/two/three-test') + expect(find('.wiki-pages')).to have_content('Three') - expect(current_path).to include('one/two/three-test') - expect(page).to have_content('Edit Page') + first(:link, text: 'Three').click - fill_in('Content', with: 'Updated Wiki Content') - click_on('Save changes') + expect(find('.nav-text')).to have_content('Three') - expect(page).to have_content('Updated Wiki Content') - end - end - end + click_on('Edit') - context 'when wiki is not empty' do - let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) } - let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: 'home', content: 'Home page' }) } + expect(current_path).to include('one/two/three-test') + expect(page).to have_content('Edit Page') - before do - visit(project_wikis_path(project)) + fill_in('Content', with: 'Updated Wiki Content') + click_on('Save changes') + + expect(page).to have_content('Updated Wiki Content') + end + end end - context 'in a user namespace' do - let(:project) { create(:project, namespace: user.namespace) } + context 'when wiki is not empty' do + let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) } + let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: 'home', content: 'Home page' }) } - it 'updates a page' do - click_link('Edit') + before do + visit(project_wikis_path(project)) + end - # Commit message field should have correct value. - expect(page).to have_field('wiki[message]', with: 'Update home') + context 'in a user namespace' do + let(:project) { create(:project, namespace: user.namespace) } - fill_in(:wiki_content, with: 'My awesome wiki!') - click_button('Save changes') + it 'updates a page' do + click_link('Edit') - expect(page).to have_content('Home') - expect(page).to have_content("Last edited by #{user.name}") - expect(page).to have_content('My awesome wiki!') - end + # Commit message field should have correct value. + expect(page).to have_field('wiki[message]', with: 'Update home') - it 'shows a validation error message' do - click_link('Edit') + fill_in(:wiki_content, with: 'My awesome wiki!') + click_button('Save changes') - fill_in(:wiki_content, with: '') - click_button('Save changes') + expect(page).to have_content('Home') + expect(page).to have_content("Last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end - 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 'shows a validation error message' do + click_link('Edit') - it 'shows the autocompletion dropdown', :js do - click_link('Edit') + fill_in(:wiki_content, with: '') + click_button('Save changes') - find('#wiki_content').native.send_keys('') - fill_in(:wiki_content, with: '@') + 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 - expect(page).to have_selector('.atwho-view') - end + it 'shows the autocompletion dropdown', :js do + click_link('Edit') - it 'shows the error message' do - click_link('Edit') + find('#wiki_content').native.send_keys('') + fill_in(:wiki_content, with: '@') - wiki_page.update(content: 'Update') + expect(page).to have_selector('.atwho-view') + end - click_button('Save changes') + it 'shows the error message' do + click_link('Edit') - expect(page).to have_content('Someone edited the page the same time you did.') - end + wiki_page.update(content: 'Update') - it 'updates a page' do - click_on('Edit') - fill_in('Content', with: 'Updated Wiki Content') - click_on('Save changes') + click_button('Save changes') - expect(page).to have_content('Updated Wiki Content') - end + expect(page).to have_content('Someone edited the page the same time you did.') + end - it 'cancels edititng of a page' do - click_on('Edit') + it 'updates a page' do + click_on('Edit') + fill_in('Content', with: 'Updated Wiki Content') + click_on('Save changes') - page.within(:css, '.wiki-form .form-actions') do - click_on('Cancel') + expect(page).to have_content('Updated Wiki Content') end - expect(current_path).to eq(project_wiki_path(project, wiki_page)) + it 'cancels edititng of a page' do + click_on('Edit') + + page.within(:css, '.wiki-form .form-actions') do + click_on('Cancel') + end + + expect(current_path).to eq(project_wiki_path(project, wiki_page)) + end end - end - context 'in a group namespace' do - let(:project) { create(:project, namespace: create(:group, :public)) } + context 'in a group namespace' do + let(:project) { create(:project, namespace: create(:group, :public)) } - it 'updates a page' do - click_link('Edit') + it 'updates a page' do + click_link('Edit') - # Commit message field should have correct value. - expect(page).to have_field('wiki[message]', with: 'Update home') + # Commit message field should have correct value. + expect(page).to have_field('wiki[message]', with: 'Update home') - fill_in(:wiki_content, with: 'My awesome wiki!') + fill_in(:wiki_content, with: 'My awesome wiki!') - click_button('Save changes') + click_button('Save changes') - expect(page).to have_content('Home') - expect(page).to have_content("Last edited by #{user.name}") - expect(page).to have_content('My awesome wiki!') + expect(page).to have_content('Home') + expect(page).to have_content("Last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end end end - end - context 'when the page is in a subdir' do - let!(:project) { create(:project, namespace: user.namespace) } - let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) } - let(:page_name) { 'page_name' } - let(:page_dir) { "foo/bar/#{page_name}" } - let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: page_dir, content: 'Home page' }) } + context 'when the page is in a subdir' do + let!(:project) { create(:project, namespace: user.namespace) } + let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) } + let(:page_name) { 'page_name' } + let(:page_dir) { "foo/bar/#{page_name}" } + let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: page_dir, content: 'Home page' }) } - before do - visit(project_wiki_edit_path(project, wiki_page)) - end + before do + visit(project_wiki_edit_path(project, wiki_page)) + end - it 'moves the page to the root folder' do - fill_in(:wiki_title, with: "/#{page_name}") + it 'moves the page to the root folder', :skip_gitaly_mock do + fill_in(:wiki_title, with: "/#{page_name}") - click_button('Save changes') + click_button('Save changes') - expect(current_path).to eq(project_wiki_path(project, page_name)) - end + expect(current_path).to eq(project_wiki_path(project, page_name)) + end - it 'moves the page to other dir' do - new_page_dir = "foo1/bar1/#{page_name}" + it 'moves the page to other dir' do + new_page_dir = "foo1/bar1/#{page_name}" - fill_in(:wiki_title, with: new_page_dir) + fill_in(:wiki_title, with: new_page_dir) - click_button('Save changes') + click_button('Save changes') - expect(current_path).to eq(project_wiki_path(project, new_page_dir)) - end + expect(current_path).to eq(project_wiki_path(project, new_page_dir)) + end - it 'remains in the same place if title has not changed' do - original_path = project_wiki_path(project, wiki_page) + it 'remains in the same place if title has not changed' do + original_path = project_wiki_path(project, wiki_page) - fill_in(:wiki_title, with: page_name) + fill_in(:wiki_title, with: page_name) - click_button('Save changes') + click_button('Save changes') - expect(current_path).to eq(original_path) - end + expect(current_path).to eq(original_path) + end - it 'can be moved to a different dir with a different name' do - new_page_dir = "foo1/bar1/new_page_name" + it 'can be moved to a different dir with a different name' do + new_page_dir = "foo1/bar1/new_page_name" - fill_in(:wiki_title, with: new_page_dir) + fill_in(:wiki_title, with: new_page_dir) - click_button('Save changes') + click_button('Save changes') - expect(current_path).to eq(project_wiki_path(project, new_page_dir)) - end + expect(current_path).to eq(project_wiki_path(project, new_page_dir)) + end - it 'can be renamed and moved to the root folder' do - new_name = 'new_page_name' + it 'can be renamed and moved to the root folder' do + new_name = 'new_page_name' - fill_in(:wiki_title, with: "/#{new_name}") + fill_in(:wiki_title, with: "/#{new_name}") - click_button('Save changes') + click_button('Save changes') - expect(current_path).to eq(project_wiki_path(project, new_name)) - end + expect(current_path).to eq(project_wiki_path(project, new_name)) + end - it 'squishes the title before creating the page' do - new_page_dir = " foo1 / bar1 / #{page_name} " + it 'squishes the title before creating the page' do + new_page_dir = " foo1 / bar1 / #{page_name} " - fill_in(:wiki_title, with: new_page_dir) + fill_in(:wiki_title, with: new_page_dir) - click_button('Save changes') + click_button('Save changes') - expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}")) + expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}")) + end end end + + context 'when Gitaly is enabled' do + it_behaves_like 'wiki page user update' + end + + context 'when Gitaly is disabled', :skip_gitaly_mock do + it_behaves_like 'wiki page user update' + end end diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb index e37436838fd..306e382119a 100644 --- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb @@ -1,146 +1,155 @@ require 'spec_helper' -# Remove skip_gitaly_mock flag when gitaly_update_page implements moving pages -describe 'User views a wiki page', :skip_gitaly_mock do - let(:user) { create(:user) } - let(:project) { create(:project, namespace: user.namespace) } - let(:wiki_page) do - create(:wiki_page, - wiki: project.wiki, - attrs: { title: 'home', content: 'Look at this [image](image.jpg)\n\n ![alt text](image.jpg)' }) - end - - before do - project.add_master(user) - sign_in(user) - end +describe 'User views a wiki page' do + shared_examples 'wiki page user view' do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + let(:wiki_page) do + create(:wiki_page, + wiki: project.wiki, + attrs: { title: 'home', content: 'Look at this [image](image.jpg)\n\n ![alt text](image.jpg)' }) + end - context 'when wiki is empty' do before do - visit(project_wikis_path(project)) + project.add_master(user) + sign_in(user) + end - click_on('New page') + context 'when wiki is empty' do + before do + visit(project_wikis_path(project)) - page.within('#modal-new-wiki') do - fill_in(:new_wiki_path, with: 'one/two/three-test') - click_on('Create page') - end + click_on('New page') - page.within('.wiki-form') do - fill_in(:wiki_content, with: 'wiki content') - click_on('Create page') + page.within('#modal-new-wiki') do + fill_in(:new_wiki_path, with: 'one/two/three-test') + click_on('Create page') + end + + page.within('.wiki-form') do + fill_in(:wiki_content, with: 'wiki content') + click_on('Create page') + end end - end - it 'shows the history of a page that has a path', :js do - expect(current_path).to include('one/two/three-test') + it 'shows the history of a page that has a path', :js do + expect(current_path).to include('one/two/three-test') - first(:link, text: 'Three').click - click_on('Page history') + first(:link, text: 'Three').click + click_on('Page history') - expect(current_path).to include('one/two/three-test') + expect(current_path).to include('one/two/three-test') - page.within(:css, '.nav-text') do - expect(page).to have_content('History') + page.within(:css, '.nav-text') do + expect(page).to have_content('History') + end end - end - it 'shows an old version of a page', :js do - expect(current_path).to include('one/two/three-test') - expect(find('.wiki-pages')).to have_content('Three') + it 'shows an old version of a page', :js do + expect(current_path).to include('one/two/three-test') + expect(find('.wiki-pages')).to have_content('Three') - first(:link, text: 'Three').click + first(:link, text: 'Three').click - expect(find('.nav-text')).to have_content('Three') + expect(find('.nav-text')).to have_content('Three') - click_on('Edit') + click_on('Edit') - expect(current_path).to include('one/two/three-test') - expect(page).to have_content('Edit Page') + expect(current_path).to include('one/two/three-test') + expect(page).to have_content('Edit Page') - fill_in('Content', with: 'Updated Wiki Content') + fill_in('Content', with: 'Updated Wiki Content') - click_on('Save changes') - click_on('Page history') + click_on('Save changes') + click_on('Page history') - page.within(:css, '.nav-text') do - expect(page).to have_content('History') - end + page.within(:css, '.nav-text') do + expect(page).to have_content('History') + end - find('a[href*="?version_id"]') + find('a[href*="?version_id"]') + end end - end - context 'when a page does not have history' do - before do - visit(project_wiki_path(project, wiki_page)) - end + context 'when a page does not have history' do + before do + visit(project_wiki_path(project, wiki_page)) + end - it 'shows all the pages' do - expect(page).to have_content(user.name) - expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize) - end + it 'shows all the pages' do + expect(page).to have_content(user.name) + expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize) + end - it 'shows a file stored in a page' do - gollum_file_double = double('Gollum::File', - mime_type: 'image/jpeg', - name: 'images/image.jpg', - path: 'images/image.jpg', - raw_data: '') - wiki_file = Gitlab::Git::WikiFile.new(gollum_file_double) + it 'shows a file stored in a page' do + gollum_file_double = double('Gollum::File', + mime_type: 'image/jpeg', + name: 'images/image.jpg', + path: 'images/image.jpg', + raw_data: '') + wiki_file = Gitlab::Git::WikiFile.new(gollum_file_double) - allow(wiki_file).to receive(:mime_type).and_return('image/jpeg') - allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file) + allow(wiki_file).to receive(:mime_type).and_return('image/jpeg') + allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file) - expect(page).to have_xpath('//img[@data-src="image.jpg"]') - expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg") + expect(page).to have_xpath('//img[@data-src="image.jpg"]') + expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg") - click_on('image') + click_on('image') - expect(current_path).to match('wikis/image.jpg') - expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved - end + expect(current_path).to match('wikis/image.jpg') + expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved + end - it 'shows the creation page if file does not exist' do - expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg") + it 'shows the creation page if file does not exist' do + expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg") - click_on('image') + click_on('image') - expect(current_path).to match('wikis/image.jpg') - expect(page).to have_content('New Wiki Page') - expect(page).to have_content('Create page') + expect(current_path).to match('wikis/image.jpg') + expect(page).to have_content('New Wiki Page') + expect(page).to have_content('Create page') + end end - end - context 'when a page has history' do - before do - wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)') - end + context 'when a page has history' do + before do + wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)') + end - it 'shows the page history' do - visit(project_wiki_path(project, wiki_page)) + it 'shows the page history' do + visit(project_wiki_path(project, wiki_page)) - expect(page).to have_selector('a.btn', text: 'Edit') + expect(page).to have_selector('a.btn', text: 'Edit') - click_on('Page history') + click_on('Page history') - expect(page).to have_content(user.name) - expect(page).to have_content("#{user.username} created page: home") - expect(page).to have_content('updated home') + expect(page).to have_content(user.name) + expect(page).to have_content("#{user.username} created page: home") + expect(page).to have_content('updated home') + end + + it 'does not show the "Edit" button' do + visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id)) + + expect(page).not_to have_selector('a.btn', text: 'Edit') + end end - it 'does not show the "Edit" button' do - visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id)) + it 'opens a default wiki page', :js do + visit(project_path(project)) - expect(page).not_to have_selector('a.btn', text: 'Edit') + find('.shortcuts-wiki').click + + expect(page).to have_content('Home · Create Page') end end - it 'opens a default wiki page', :js do - visit(project_path(project)) - - find('.shortcuts-wiki').click + context 'when Gitaly is enabled' do + it_behaves_like 'wiki page user view' + end - expect(page).to have_content('Home · Create Page') + context 'when Gitaly is disabled', :skip_gitaly_mock do + it_behaves_like 'wiki page user view' end end -- cgit v1.2.1 From 3d4c40a014e045d68ac80b6ebe2a4e498805a261 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Wed, 7 Feb 2018 21:45:03 +0000 Subject: Replace $.ajax in profile.js with axios --- spec/features/profiles/user_visits_profile_spec.rb | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'spec/features') diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb index 6601d3039ed..a5d80439143 100644 --- a/spec/features/profiles/user_visits_profile_spec.rb +++ b/spec/features/profiles/user_visits_profile_spec.rb @@ -12,4 +12,13 @@ describe 'User visits their profile' do it 'shows correct menu item' do expect(page).to have_active_navigation('Profile') end + + describe 'profile settings', :js do + it 'saves updates' do + fill_in 'user_bio', with: 'bio' + click_button 'Update profile settings' + + expect(page).to have_content('Profile was successfully updated') + end + end end -- cgit v1.2.1 From 97f08c651e0c95e33dee4a529ff80472e7d3bbe4 Mon Sep 17 00:00:00 2001 From: Shah El-Rahman Date: Tue, 6 Feb 2018 21:05:54 -0600 Subject: Add modal for stopping and retrying pipelines Fix tests Address code review feedback Fix tests --- spec/features/projects/pipelines/pipelines_spec.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'spec/features') diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 592c99fc64a..37a06b65481 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -109,7 +109,8 @@ describe 'Pipelines', :js do context 'when canceling' do before do - accept_confirm { find('.js-pipelines-cancel-button').click } + find('.js-pipelines-cancel-button').click + find('.js-primary-button').click wait_for_requests end @@ -140,6 +141,7 @@ describe 'Pipelines', :js do context 'when retrying' do before do find('.js-pipelines-retry-button').click + find('.js-primary-button').click wait_for_requests end @@ -238,7 +240,8 @@ describe 'Pipelines', :js do context 'when canceling' do before do - accept_alert { find('.js-pipelines-cancel-button').click } + find('.js-pipelines-cancel-button').click + find('.js-primary-button').click end it 'indicates that pipeline was canceled' do -- cgit v1.2.1 From 3cb19dd42c097ebd1210bbf7961f9ef000d784cb Mon Sep 17 00:00:00 2001 From: Shah El-Rahman Date: Thu, 8 Feb 2018 05:17:12 +0000 Subject: Resolve "New design for user deletion confirmation in admin area" --- spec/features/admin/admin_users_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'spec/features') diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index a69b428d117..2307ba5985e 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -26,8 +26,8 @@ describe "Admin::Users" do expect(page).to have_content(user.email) expect(page).to have_content(user.name) expect(page).to have_link('Block', href: block_admin_user_path(user)) - expect(page).to have_link('Remove user', href: admin_user_path(user)) - expect(page).to have_link('Remove user and contributions', href: admin_user_path(user, hard_delete: true)) + expect(page).to have_button('Delete user') + expect(page).to have_button('Delete user and contributions') end describe 'Two-factor Authentication filters' do @@ -122,8 +122,8 @@ describe "Admin::Users" do expect(page).to have_content(user.email) expect(page).to have_content(user.name) expect(page).to have_link('Block user', href: block_admin_user_path(user)) - expect(page).to have_link('Remove user', href: admin_user_path(user)) - expect(page).to have_link('Remove user and contributions', href: admin_user_path(user, hard_delete: true)) + expect(page).to have_button('Delete user') + expect(page).to have_button('Delete user and contributions') end describe 'Impersonation' do -- cgit v1.2.1 From d4e232aeb3d7264af10a2bfb69027ddc61ab1067 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 8 Feb 2018 16:14:31 +0000 Subject: Added 'clear' button to ci lint editor --- spec/features/ci_lint_spec.rb | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'spec/features') diff --git a/spec/features/ci_lint_spec.rb b/spec/features/ci_lint_spec.rb index 9bc23baf6cf..b1dceec9da8 100644 --- a/spec/features/ci_lint_spec.rb +++ b/spec/features/ci_lint_spec.rb @@ -3,16 +3,19 @@ require 'spec_helper' describe 'CI Lint', :js do before do sign_in(create(:user)) + + visit ci_lint_path + find('#ci-editor') + execute_script("ace.edit('ci-editor').setValue(#{yaml_content.to_json});") + + # Ace editor updates a hidden textarea and it happens asynchronously + wait_for('YAML content') do + find('.ace_content').text.present? + end end describe 'YAML parsing' do before do - visit ci_lint_path - # Ace editor updates a hidden textarea and it happens asynchronously - # `sleep 0.1` is actually needed here because of this - find('#ci-editor') - execute_script("ace.edit('ci-editor').setValue(" + yaml_content.to_json + ");") - sleep 0.1 click_on 'Validate' end @@ -32,11 +35,10 @@ describe 'CI Lint', :js do end context 'YAML is incorrect' do - let(:yaml_content) { '' } + let(:yaml_content) { 'value: cannot have :' } it 'displays information about an error' do expect(page).to have_content('Status: syntax is incorrect') - expect(page).to have_content('Error: Please provide content of .gitlab-ci.yml') end end @@ -48,4 +50,20 @@ describe 'CI Lint', :js do end end end + + describe 'YAML clearing' do + before do + click_on 'Clear' + end + + context 'YAML is present' do + let(:yaml_content) do + File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + end + + it 'YAML content is cleared' do + expect(page).to have_field('content', with: '', visible: false, type: 'textarea') + end + end + end end -- cgit v1.2.1 From 603fa7c14193d37e3953225501d2108f0c581df5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 31 Jan 2018 21:48:18 +0000 Subject: Merge branch 'fix-mermaid-xss' into 'security-10-4' [10.4] Fix stored XSS in code blocks --- spec/features/copy_as_gfm_spec.rb | 782 --------------------- spec/features/gitlab_flavored_markdown_spec.rb | 133 ---- spec/features/markdown/copy_as_gfm_spec.rb | 782 +++++++++++++++++++++ .../markdown/gitlab_flavored_markdown_spec.rb | 133 ++++ spec/features/markdown/markdown_spec.rb | 337 +++++++++ spec/features/markdown/math_spec.rb | 22 + spec/features/markdown/mermaid_spec.rb | 24 + spec/features/markdown_spec.rb | 337 --------- 8 files changed, 1298 insertions(+), 1252 deletions(-) delete mode 100644 spec/features/copy_as_gfm_spec.rb delete mode 100644 spec/features/gitlab_flavored_markdown_spec.rb create mode 100644 spec/features/markdown/copy_as_gfm_spec.rb create mode 100644 spec/features/markdown/gitlab_flavored_markdown_spec.rb create mode 100644 spec/features/markdown/markdown_spec.rb create mode 100644 spec/features/markdown/math_spec.rb create mode 100644 spec/features/markdown/mermaid_spec.rb delete mode 100644 spec/features/markdown_spec.rb (limited to 'spec/features') diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb deleted file mode 100644 index f82ed6300cc..00000000000 --- a/spec/features/copy_as_gfm_spec.rb +++ /dev/null @@ -1,782 +0,0 @@ -require 'spec_helper' - -describe 'Copy as GFM', :js do - include MarkupHelper - include RepoHelpers - include ActionView::Helpers::JavaScriptHelper - - before do - sign_in(create(:admin)) - end - - describe 'Copying rendered GFM' do - before do - @feat = MarkdownFeature.new - - # `markdown` helper expects a `@project` variable - @project = @feat.project - - visit project_issue_path(@project, @feat.issue) - end - - # The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert GitLab Flavored Markdown (GFM) to HTML. - # The handlers defined in app/assets/javascripts/copy_as_gfm.js consequently convert that same HTML to GFM. - # To make sure these filters and handlers are properly aligned, this spec tests the GFM-to-HTML-to-GFM cycle - # by verifying (`html_to_gfm(gfm_to_html(gfm)) == gfm`) for a number of examples of GFM for every filter, using the `verify` helper. - - # These are all in a single `it` for performance reasons. - it 'works', :aggregate_failures do - verify( - 'nesting', - - '> 1. [x] **[$`2 + 2`$ {-=-}{+=+} 2^2 ~~:thumbsup:~~](http://google.com)**' - ) - - verify( - 'a real world example from the gitlab-ce README', - - <<-GFM.strip_heredoc - # GitLab - - [![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) - [![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) - [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) - [![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42) - - ## Canonical source - - The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/). - - ## Open source software to collaborate on code - - To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/). - - - Manage Git repositories with fine grained access controls that keep your code secure - - - Perform code reviews and enhance collaboration with merge requests - - - Complete continuous integration (CI) and CD pipelines to builds, test, and deploy your applications - - - Each project can also have an issue tracker, issue board, and a wiki - - - Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises - - - Completely free and open source (MIT Expat license) - GFM - ) - - aggregate_failures('an accidentally selected empty element') do - gfm = '# Heading1' - - html = <<-HTML.strip_heredoc -

Heading1

- -

- HTML - - output_gfm = html_to_gfm(html) - expect(output_gfm.strip).to eq(gfm.strip) - end - - aggregate_failures('an accidentally selected other element') do - gfm = 'Test comment with **Markdown!**' - - html = <<-HTML.strip_heredoc -
  • -
    -

    - Test comment with Markdown! -

    -
    -
  • - -
  • - HTML - - output_gfm = html_to_gfm(html) - expect(output_gfm.strip).to eq(gfm.strip) - end - - verify( - 'InlineDiffFilter', - - '{-Deleted text-}', - '{+Added text+}' - ) - - verify( - 'TaskListFilter', - - '- [ ] Unchecked task', - '- [x] Checked task', - '1. [ ] Unchecked numbered task', - '1. [x] Checked numbered task' - ) - - verify( - 'ReferenceFilter', - - # issue reference - @feat.issue.to_reference, - # full issue reference - @feat.issue.to_reference(full: true), - # issue URL - project_issue_url(@project, @feat.issue), - # issue URL with note anchor - project_issue_url(@project, @feat.issue, anchor: 'note_123'), - # issue link - "[Issue](#{project_issue_url(@project, @feat.issue)})", - # issue link with note anchor - "[Issue](#{project_issue_url(@project, @feat.issue, anchor: 'note_123')})" - ) - - verify( - 'AutolinkFilter', - - 'https://example.com' - ) - - verify( - 'TableOfContentsFilter', - - '[[_TOC_]]' - ) - - verify( - 'EmojiFilter', - - ':thumbsup:' - ) - - verify( - 'ImageLinkFilter', - - '![Image](https://example.com/image.png)' - ) - - verify( - 'VideoLinkFilter', - - '![Video](https://example.com/video.mp4)' - ) - - verify( - 'MathFilter: math as converted from GFM to HTML', - - '$`c = \pm\sqrt{a^2 + b^2}`$', - - # math block - <<-GFM.strip_heredoc - ```math - c = \pm\sqrt{a^2 + b^2} - ``` - GFM - ) - - aggregate_failures('MathFilter: math as transformed from HTML to KaTeX') do - gfm = '$`c = \pm\sqrt{a^2 + b^2}`$' - - html = <<-HTML.strip_heredoc - - - - - - c - = - ± - - - - a - 2 - - + - - b - 2 - - - - - c = \\pm\\sqrt{a^2 + b^2} - - - - - - HTML - - output_gfm = html_to_gfm(html) - expect(output_gfm.strip).to eq(gfm.strip) - end - - verify( - 'MermaidFilter: mermaid as converted from GFM to HTML', - - <<-GFM.strip_heredoc - ```mermaid - graph TD; - A-->B; - ``` - GFM - ) - - aggregate_failures('MermaidFilter: mermaid as transformed from HTML to SVG') do - gfm = <<-GFM.strip_heredoc - ```mermaid - graph TD; - A-->B; - ``` - GFM - - html = <<-HTML.strip_heredoc - - - - - - - - - - - - - - - - - - - -
    - -
    -
    -
    -
    -
    - - - - - - -
    A
    -
    -
    -
    -
    - - - - - - -
    B
    -
    -
    -
    -
    -
    -
    -
    - graph TD; - A-->B; - -
    - HTML - - output_gfm = html_to_gfm(html) - expect(output_gfm.strip).to eq(gfm.strip) - end - - verify( - 'SanitizationFilter', - - <<-GFM.strip_heredoc - sub - -
    -
    dt
    -
    dd
    -
    - - kbd - - q - - samp - - var - - ruby - - rt - - rp - - abbr - - summary - -
    details
    - GFM - ) - - verify( - 'SanitizationFilter', - - <<-GFM.strip_heredoc, - ``` - Plain text - ``` - GFM - - <<-GFM.strip_heredoc, - ```ruby - def foo - bar - end - ``` - GFM - - <<-GFM.strip_heredoc - Foo - - This is an example of GFM - - ```js - Code goes here - ``` - GFM - ) - - verify( - 'MarkdownFilter', - - "Line with two spaces at the end \nto insert a linebreak", - - '`code`', - '`` code with ` ticks ``', - - '> Quote', - - # multiline quote - <<-GFM.strip_heredoc, - > Multiline - > Quote - > - > With multiple paragraphs - GFM - - '![Image](https://example.com/image.png)', - - '# Heading with no anchor link', - - '[Link](https://example.com)', - - '- List item', - - # multiline list item - <<-GFM.strip_heredoc, - - Multiline - List item - GFM - - # nested lists - <<-GFM.strip_heredoc, - - Nested - - - Lists - GFM - - # list with blockquote - <<-GFM.strip_heredoc, - - List - - > Blockquote - GFM - - '1. Numbered list item', - - # multiline numbered list item - <<-GFM.strip_heredoc, - 1. Multiline - Numbered list item - GFM - - # nested numbered list - <<-GFM.strip_heredoc, - 1. Nested - - 1. Numbered lists - GFM - - '# Heading', - '## Heading', - '### Heading', - '#### Heading', - '##### Heading', - '###### Heading', - - '**Bold**', - - '_Italics_', - - '~~Strikethrough~~', - - '2^2', - - '-----', - - # table - <<-GFM.strip_heredoc, - | Centered | Right | Left | - |:--------:|------:|------| - | Foo | Bar | **Baz** | - | Foo | Bar | **Baz** | - GFM - - # table with empty heading - <<-GFM.strip_heredoc, - | | x | y | - |---|---|---| - | a | 1 | 0 | - | b | 0 | 1 | - GFM - ) - end - - alias_method :gfm_to_html, :markdown - - def verify(label, *gfms) - aggregate_failures(label) do - gfms.each do |gfm| - html = gfm_to_html(gfm).gsub(/\A | \z/, '') - output_gfm = html_to_gfm(html) - expect(output_gfm.strip).to eq(gfm.strip) - end - end - end - - # Fake a `current_user` helper - def current_user - @feat.user - end - end - - describe 'Copying code' do - let(:project) { create(:project, :repository) } - - context 'from a diff' do - shared_examples 'copying code from a diff' do - context 'selecting one word of text' do - it 'copies as inline code' do - verify( - '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"] .line .no', - - '`RuntimeError`', - - target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]' - ) - end - end - - context 'selecting one line of text' do - it 'copies as inline code' do - verify( - '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]', - - '`raise RuntimeError, "System commands must be given as an array of strings"`', - - target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]' - ) - end - end - - context 'selecting multiple lines of text' do - it 'copies as a code block' do - verify( - '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', - - <<-GFM.strip_heredoc, - ```ruby - raise RuntimeError, "System commands must be given as an array of strings" - end - ``` - GFM - - target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]' - ) - end - end - end - - context 'inline diff' do - before do - visit project_commit_path(project, sample_commit.id, view: 'inline') - end - - it_behaves_like 'copying code from a diff' - end - - context 'parallel diff' do - before do - visit project_commit_path(project, sample_commit.id, view: 'parallel') - end - - it_behaves_like 'copying code from a diff' - - context 'selecting code on the left' do - it 'copies as a code block' do - verify( - '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', - - <<-GFM.strip_heredoc, - ```ruby - unless cmd.is_a?(Array) - raise "System commands must be given as an array of strings" - end - ``` - GFM - - target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].left-side' - ) - end - end - - context 'selecting code on the right' do - it 'copies as a code block' do - verify( - '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', - - <<-GFM.strip_heredoc, - ```ruby - unless cmd.is_a?(Array) - raise RuntimeError, "System commands must be given as an array of strings" - end - ``` - GFM - - target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].right-side' - ) - end - end - end - end - - context 'from a blob' do - before do - visit project_blob_path(project, File.join('master', 'files/ruby/popen.rb')) - wait_for_requests - end - - context 'selecting one word of text' do - it 'copies as inline code' do - verify( - '.line[id="LC9"] .no', - - '`RuntimeError`' - ) - end - end - - context 'selecting one line of text' do - it 'copies as inline code' do - verify( - '.line[id="LC9"]', - - '`raise RuntimeError, "System commands must be given as an array of strings"`' - ) - end - end - - context 'selecting multiple lines of text' do - it 'copies as a code block' do - verify( - '.line[id="LC9"], .line[id="LC10"]', - - <<-GFM.strip_heredoc, - ```ruby - raise RuntimeError, "System commands must be given as an array of strings" - end - ``` - GFM - ) - end - end - end - - context 'from a GFM code block' do - before do - visit project_blob_path(project, File.join('markdown', 'doc/api/users.md')) - wait_for_requests - end - - context 'selecting one word of text' do - it 'copies as inline code' do - verify( - '.line[id="LC27"] .s2', - - '`"bio"`' - ) - end - end - - context 'selecting one line of text' do - it 'copies as inline code' do - verify( - '.line[id="LC27"]', - - '`"bio": null,`' - ) - end - end - - context 'selecting multiple lines of text' do - it 'copies as a code block with the correct language' do - verify( - '.line[id="LC27"], .line[id="LC28"]', - - <<-GFM.strip_heredoc, - ```json - "bio": null, - "skype": "", - ``` - GFM - ) - end - end - end - - def verify(selector, gfm, target: nil) - html = html_for_selector(selector) - output_gfm = html_to_gfm(html, 'transformCodeSelection', target: target) - expect(output_gfm.strip).to eq(gfm.strip) - end - end - - def html_for_selector(selector) - js = <<-JS.strip_heredoc - (function(selector) { - var els = document.querySelectorAll(selector); - var htmls = [].slice.call(els).map(function(el) { return el.outerHTML; }); - return htmls.join("\\n"); - })("#{escape_javascript(selector)}") - JS - page.evaluate_script(js) - end - - def html_to_gfm(html, transformer = 'transformGFMSelection', target: nil) - js = <<-JS.strip_heredoc - (function(html) { - var transformer = window.CopyAsGFM[#{transformer.inspect}]; - - var node = document.createElement('div'); - $(html).each(function() { node.appendChild(this) }); - - var targetSelector = #{target.to_json}; - var target; - if (targetSelector) { - target = document.querySelector(targetSelector); - } - - node = transformer(node, target); - if (!node) return null; - - return window.CopyAsGFM.nodeToGFM(node); - })("#{escape_javascript(html)}") - JS - page.evaluate_script(js) - end -end diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb deleted file mode 100644 index 3c2186b3598..00000000000 --- a/spec/features/gitlab_flavored_markdown_spec.rb +++ /dev/null @@ -1,133 +0,0 @@ -require 'spec_helper' - -describe "GitLab Flavored Markdown" do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:issue) { create(:issue, project: project) } - let(:fred) do - create(:user, name: 'fred') do |user| - project.add_master(user) - end - end - - before do - sign_in(user) - project.add_developer(user) - end - - describe "for commits" do - let(:project) { create(:project, :repository) } - let(:commit) { project.commit } - - before do - allow_any_instance_of(Commit).to receive(:title) - .and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details") - end - - it "renders title in commits#index" do - visit project_commits_path(project, 'master', limit: 1) - - expect(page).to have_link(issue.to_reference) - end - - it "renders title in commits#show" do - visit project_commit_path(project, commit) - - expect(page).to have_link(issue.to_reference) - end - - it "renders description in commits#show" do - visit project_commit_path(project, commit) - - expect(page).to have_link(fred.to_reference) - end - - it "renders title in repositories#branches" do - visit project_branches_path(project) - - expect(page).to have_link(issue.to_reference) - end - end - - describe "for issues", :js do - before do - @other_issue = create(:issue, - author: user, - assignees: [user], - project: project) - @issue = create(:issue, - author: user, - assignees: [user], - project: project, - title: "fix #{@other_issue.to_reference}", - description: "ask #{fred.to_reference} for details") - - @note = create(:note_on_issue, noteable: @issue, project: @issue.project, note: "Hello world") - end - - it "renders subject in issues#index" do - visit project_issues_path(project) - - expect(page).to have_link(@other_issue.to_reference) - end - - it "renders subject in issues#show" do - visit project_issue_path(project, @issue) - - expect(page).to have_link(@other_issue.to_reference) - end - - it "renders details in issues#show" do - visit project_issue_path(project, @issue) - - expect(page).to have_link(fred.to_reference) - end - end - - describe "for merge requests" do - let(:project) { create(:project, :repository) } - - before do - @merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix #{issue.to_reference}") - end - - it "renders title in merge_requests#index" do - visit project_merge_requests_path(project) - - expect(page).to have_link(issue.to_reference) - end - - it "renders title in merge_requests#show" do - visit project_merge_request_path(project, @merge_request) - - expect(page).to have_link(issue.to_reference) - end - end - - describe "for milestones" do - before do - @milestone = create(:milestone, - project: project, - title: "fix #{issue.to_reference}", - description: "ask #{fred.to_reference} for details") - end - - it "renders title in milestones#index" do - visit project_milestones_path(project) - - expect(page).to have_link(issue.to_reference) - end - - it "renders title in milestones#show" do - visit project_milestone_path(project, @milestone) - - expect(page).to have_link(issue.to_reference) - end - - it "renders description in milestones#show" do - visit project_milestone_path(project, @milestone) - - expect(page).to have_link(fred.to_reference) - end - end -end diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb new file mode 100644 index 00000000000..f82ed6300cc --- /dev/null +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -0,0 +1,782 @@ +require 'spec_helper' + +describe 'Copy as GFM', :js do + include MarkupHelper + include RepoHelpers + include ActionView::Helpers::JavaScriptHelper + + before do + sign_in(create(:admin)) + end + + describe 'Copying rendered GFM' do + before do + @feat = MarkdownFeature.new + + # `markdown` helper expects a `@project` variable + @project = @feat.project + + visit project_issue_path(@project, @feat.issue) + end + + # The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert GitLab Flavored Markdown (GFM) to HTML. + # The handlers defined in app/assets/javascripts/copy_as_gfm.js consequently convert that same HTML to GFM. + # To make sure these filters and handlers are properly aligned, this spec tests the GFM-to-HTML-to-GFM cycle + # by verifying (`html_to_gfm(gfm_to_html(gfm)) == gfm`) for a number of examples of GFM for every filter, using the `verify` helper. + + # These are all in a single `it` for performance reasons. + it 'works', :aggregate_failures do + verify( + 'nesting', + + '> 1. [x] **[$`2 + 2`$ {-=-}{+=+} 2^2 ~~:thumbsup:~~](http://google.com)**' + ) + + verify( + 'a real world example from the gitlab-ce README', + + <<-GFM.strip_heredoc + # GitLab + + [![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) + [![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) + [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) + [![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42) + + ## Canonical source + + The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/). + + ## Open source software to collaborate on code + + To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/). + + - Manage Git repositories with fine grained access controls that keep your code secure + + - Perform code reviews and enhance collaboration with merge requests + + - Complete continuous integration (CI) and CD pipelines to builds, test, and deploy your applications + + - Each project can also have an issue tracker, issue board, and a wiki + + - Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises + + - Completely free and open source (MIT Expat license) + GFM + ) + + aggregate_failures('an accidentally selected empty element') do + gfm = '# Heading1' + + html = <<-HTML.strip_heredoc +

    Heading1

    + +

    + HTML + + output_gfm = html_to_gfm(html) + expect(output_gfm.strip).to eq(gfm.strip) + end + + aggregate_failures('an accidentally selected other element') do + gfm = 'Test comment with **Markdown!**' + + html = <<-HTML.strip_heredoc +
  • +
    +

    + Test comment with Markdown! +

    +
    +
  • + +
  • + HTML + + output_gfm = html_to_gfm(html) + expect(output_gfm.strip).to eq(gfm.strip) + end + + verify( + 'InlineDiffFilter', + + '{-Deleted text-}', + '{+Added text+}' + ) + + verify( + 'TaskListFilter', + + '- [ ] Unchecked task', + '- [x] Checked task', + '1. [ ] Unchecked numbered task', + '1. [x] Checked numbered task' + ) + + verify( + 'ReferenceFilter', + + # issue reference + @feat.issue.to_reference, + # full issue reference + @feat.issue.to_reference(full: true), + # issue URL + project_issue_url(@project, @feat.issue), + # issue URL with note anchor + project_issue_url(@project, @feat.issue, anchor: 'note_123'), + # issue link + "[Issue](#{project_issue_url(@project, @feat.issue)})", + # issue link with note anchor + "[Issue](#{project_issue_url(@project, @feat.issue, anchor: 'note_123')})" + ) + + verify( + 'AutolinkFilter', + + 'https://example.com' + ) + + verify( + 'TableOfContentsFilter', + + '[[_TOC_]]' + ) + + verify( + 'EmojiFilter', + + ':thumbsup:' + ) + + verify( + 'ImageLinkFilter', + + '![Image](https://example.com/image.png)' + ) + + verify( + 'VideoLinkFilter', + + '![Video](https://example.com/video.mp4)' + ) + + verify( + 'MathFilter: math as converted from GFM to HTML', + + '$`c = \pm\sqrt{a^2 + b^2}`$', + + # math block + <<-GFM.strip_heredoc + ```math + c = \pm\sqrt{a^2 + b^2} + ``` + GFM + ) + + aggregate_failures('MathFilter: math as transformed from HTML to KaTeX') do + gfm = '$`c = \pm\sqrt{a^2 + b^2}`$' + + html = <<-HTML.strip_heredoc + + + + + + c + = + ± + + + + a + 2 + + + + + b + 2 + + + + + c = \\pm\\sqrt{a^2 + b^2} + + + + + + HTML + + output_gfm = html_to_gfm(html) + expect(output_gfm.strip).to eq(gfm.strip) + end + + verify( + 'MermaidFilter: mermaid as converted from GFM to HTML', + + <<-GFM.strip_heredoc + ```mermaid + graph TD; + A-->B; + ``` + GFM + ) + + aggregate_failures('MermaidFilter: mermaid as transformed from HTML to SVG') do + gfm = <<-GFM.strip_heredoc + ```mermaid + graph TD; + A-->B; + ``` + GFM + + html = <<-HTML.strip_heredoc + + + + + + + + + + + + + + + + + + + +
    + +
    +
    +
    +
    +
    + + + + + + +
    A
    +
    +
    +
    +
    + + + + + + +
    B
    +
    +
    +
    +
    +
    +
    +
    + graph TD; + A-->B; + +
    + HTML + + output_gfm = html_to_gfm(html) + expect(output_gfm.strip).to eq(gfm.strip) + end + + verify( + 'SanitizationFilter', + + <<-GFM.strip_heredoc + sub + +
    +
    dt
    +
    dd
    +
    + + kbd + + q + + samp + + var + + ruby + + rt + + rp + + abbr + + summary + +
    details
    + GFM + ) + + verify( + 'SanitizationFilter', + + <<-GFM.strip_heredoc, + ``` + Plain text + ``` + GFM + + <<-GFM.strip_heredoc, + ```ruby + def foo + bar + end + ``` + GFM + + <<-GFM.strip_heredoc + Foo + + This is an example of GFM + + ```js + Code goes here + ``` + GFM + ) + + verify( + 'MarkdownFilter', + + "Line with two spaces at the end \nto insert a linebreak", + + '`code`', + '`` code with ` ticks ``', + + '> Quote', + + # multiline quote + <<-GFM.strip_heredoc, + > Multiline + > Quote + > + > With multiple paragraphs + GFM + + '![Image](https://example.com/image.png)', + + '# Heading with no anchor link', + + '[Link](https://example.com)', + + '- List item', + + # multiline list item + <<-GFM.strip_heredoc, + - Multiline + List item + GFM + + # nested lists + <<-GFM.strip_heredoc, + - Nested + + - Lists + GFM + + # list with blockquote + <<-GFM.strip_heredoc, + - List + + > Blockquote + GFM + + '1. Numbered list item', + + # multiline numbered list item + <<-GFM.strip_heredoc, + 1. Multiline + Numbered list item + GFM + + # nested numbered list + <<-GFM.strip_heredoc, + 1. Nested + + 1. Numbered lists + GFM + + '# Heading', + '## Heading', + '### Heading', + '#### Heading', + '##### Heading', + '###### Heading', + + '**Bold**', + + '_Italics_', + + '~~Strikethrough~~', + + '2^2', + + '-----', + + # table + <<-GFM.strip_heredoc, + | Centered | Right | Left | + |:--------:|------:|------| + | Foo | Bar | **Baz** | + | Foo | Bar | **Baz** | + GFM + + # table with empty heading + <<-GFM.strip_heredoc, + | | x | y | + |---|---|---| + | a | 1 | 0 | + | b | 0 | 1 | + GFM + ) + end + + alias_method :gfm_to_html, :markdown + + def verify(label, *gfms) + aggregate_failures(label) do + gfms.each do |gfm| + html = gfm_to_html(gfm).gsub(/\A | \z/, '') + output_gfm = html_to_gfm(html) + expect(output_gfm.strip).to eq(gfm.strip) + end + end + end + + # Fake a `current_user` helper + def current_user + @feat.user + end + end + + describe 'Copying code' do + let(:project) { create(:project, :repository) } + + context 'from a diff' do + shared_examples 'copying code from a diff' do + context 'selecting one word of text' do + it 'copies as inline code' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"] .line .no', + + '`RuntimeError`', + + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]' + ) + end + end + + context 'selecting one line of text' do + it 'copies as inline code' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]', + + '`raise RuntimeError, "System commands must be given as an array of strings"`', + + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]' + ) + end + end + + context 'selecting multiple lines of text' do + it 'copies as a code block' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', + + <<-GFM.strip_heredoc, + ```ruby + raise RuntimeError, "System commands must be given as an array of strings" + end + ``` + GFM + + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]' + ) + end + end + end + + context 'inline diff' do + before do + visit project_commit_path(project, sample_commit.id, view: 'inline') + end + + it_behaves_like 'copying code from a diff' + end + + context 'parallel diff' do + before do + visit project_commit_path(project, sample_commit.id, view: 'parallel') + end + + it_behaves_like 'copying code from a diff' + + context 'selecting code on the left' do + it 'copies as a code block' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', + + <<-GFM.strip_heredoc, + ```ruby + unless cmd.is_a?(Array) + raise "System commands must be given as an array of strings" + end + ``` + GFM + + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].left-side' + ) + end + end + + context 'selecting code on the right' do + it 'copies as a code block' do + verify( + '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"], [id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"]', + + <<-GFM.strip_heredoc, + ```ruby + unless cmd.is_a?(Array) + raise RuntimeError, "System commands must be given as an array of strings" + end + ``` + GFM + + target: '[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"].right-side' + ) + end + end + end + end + + context 'from a blob' do + before do + visit project_blob_path(project, File.join('master', 'files/ruby/popen.rb')) + wait_for_requests + end + + context 'selecting one word of text' do + it 'copies as inline code' do + verify( + '.line[id="LC9"] .no', + + '`RuntimeError`' + ) + end + end + + context 'selecting one line of text' do + it 'copies as inline code' do + verify( + '.line[id="LC9"]', + + '`raise RuntimeError, "System commands must be given as an array of strings"`' + ) + end + end + + context 'selecting multiple lines of text' do + it 'copies as a code block' do + verify( + '.line[id="LC9"], .line[id="LC10"]', + + <<-GFM.strip_heredoc, + ```ruby + raise RuntimeError, "System commands must be given as an array of strings" + end + ``` + GFM + ) + end + end + end + + context 'from a GFM code block' do + before do + visit project_blob_path(project, File.join('markdown', 'doc/api/users.md')) + wait_for_requests + end + + context 'selecting one word of text' do + it 'copies as inline code' do + verify( + '.line[id="LC27"] .s2', + + '`"bio"`' + ) + end + end + + context 'selecting one line of text' do + it 'copies as inline code' do + verify( + '.line[id="LC27"]', + + '`"bio": null,`' + ) + end + end + + context 'selecting multiple lines of text' do + it 'copies as a code block with the correct language' do + verify( + '.line[id="LC27"], .line[id="LC28"]', + + <<-GFM.strip_heredoc, + ```json + "bio": null, + "skype": "", + ``` + GFM + ) + end + end + end + + def verify(selector, gfm, target: nil) + html = html_for_selector(selector) + output_gfm = html_to_gfm(html, 'transformCodeSelection', target: target) + expect(output_gfm.strip).to eq(gfm.strip) + end + end + + def html_for_selector(selector) + js = <<-JS.strip_heredoc + (function(selector) { + var els = document.querySelectorAll(selector); + var htmls = [].slice.call(els).map(function(el) { return el.outerHTML; }); + return htmls.join("\\n"); + })("#{escape_javascript(selector)}") + JS + page.evaluate_script(js) + end + + def html_to_gfm(html, transformer = 'transformGFMSelection', target: nil) + js = <<-JS.strip_heredoc + (function(html) { + var transformer = window.CopyAsGFM[#{transformer.inspect}]; + + var node = document.createElement('div'); + $(html).each(function() { node.appendChild(this) }); + + var targetSelector = #{target.to_json}; + var target; + if (targetSelector) { + target = document.querySelector(targetSelector); + } + + node = transformer(node, target); + if (!node) return null; + + return window.CopyAsGFM.nodeToGFM(node); + })("#{escape_javascript(html)}") + JS + page.evaluate_script(js) + end +end diff --git a/spec/features/markdown/gitlab_flavored_markdown_spec.rb b/spec/features/markdown/gitlab_flavored_markdown_spec.rb new file mode 100644 index 00000000000..3c2186b3598 --- /dev/null +++ b/spec/features/markdown/gitlab_flavored_markdown_spec.rb @@ -0,0 +1,133 @@ +require 'spec_helper' + +describe "GitLab Flavored Markdown" do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:issue) { create(:issue, project: project) } + let(:fred) do + create(:user, name: 'fred') do |user| + project.add_master(user) + end + end + + before do + sign_in(user) + project.add_developer(user) + end + + describe "for commits" do + let(:project) { create(:project, :repository) } + let(:commit) { project.commit } + + before do + allow_any_instance_of(Commit).to receive(:title) + .and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details") + end + + it "renders title in commits#index" do + visit project_commits_path(project, 'master', limit: 1) + + expect(page).to have_link(issue.to_reference) + end + + it "renders title in commits#show" do + visit project_commit_path(project, commit) + + expect(page).to have_link(issue.to_reference) + end + + it "renders description in commits#show" do + visit project_commit_path(project, commit) + + expect(page).to have_link(fred.to_reference) + end + + it "renders title in repositories#branches" do + visit project_branches_path(project) + + expect(page).to have_link(issue.to_reference) + end + end + + describe "for issues", :js do + before do + @other_issue = create(:issue, + author: user, + assignees: [user], + project: project) + @issue = create(:issue, + author: user, + assignees: [user], + project: project, + title: "fix #{@other_issue.to_reference}", + description: "ask #{fred.to_reference} for details") + + @note = create(:note_on_issue, noteable: @issue, project: @issue.project, note: "Hello world") + end + + it "renders subject in issues#index" do + visit project_issues_path(project) + + expect(page).to have_link(@other_issue.to_reference) + end + + it "renders subject in issues#show" do + visit project_issue_path(project, @issue) + + expect(page).to have_link(@other_issue.to_reference) + end + + it "renders details in issues#show" do + visit project_issue_path(project, @issue) + + expect(page).to have_link(fred.to_reference) + end + end + + describe "for merge requests" do + let(:project) { create(:project, :repository) } + + before do + @merge_request = create(:merge_request, source_project: project, target_project: project, title: "fix #{issue.to_reference}") + end + + it "renders title in merge_requests#index" do + visit project_merge_requests_path(project) + + expect(page).to have_link(issue.to_reference) + end + + it "renders title in merge_requests#show" do + visit project_merge_request_path(project, @merge_request) + + expect(page).to have_link(issue.to_reference) + end + end + + describe "for milestones" do + before do + @milestone = create(:milestone, + project: project, + title: "fix #{issue.to_reference}", + description: "ask #{fred.to_reference} for details") + end + + it "renders title in milestones#index" do + visit project_milestones_path(project) + + expect(page).to have_link(issue.to_reference) + end + + it "renders title in milestones#show" do + visit project_milestone_path(project, @milestone) + + expect(page).to have_link(issue.to_reference) + end + + it "renders description in milestones#show" do + visit project_milestone_path(project, @milestone) + + expect(page).to have_link(fred.to_reference) + end + end +end diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb new file mode 100644 index 00000000000..f13d78d24e3 --- /dev/null +++ b/spec/features/markdown/markdown_spec.rb @@ -0,0 +1,337 @@ +require 'spec_helper' +require 'erb' + +# This feature spec is intended to be a comprehensive exercising of all of +# GitLab's non-standard Markdown parsing and the integration thereof. +# +# These tests should be very high-level. Anything low-level belongs in the specs +# for the corresponding HTML::Pipeline filter or helper method. +# +# The idea is to pass a Markdown document through our entire processing stack. +# +# The process looks like this: +# +# Raw Markdown +# -> `markdown` helper +# -> Redcarpet::Render::GitlabHTML converts Markdown to HTML +# -> Post-process HTML +# -> `gfm` helper +# -> HTML::Pipeline +# -> SanitizationFilter +# -> Other filters, depending on pipeline +# -> `html_safe` +# -> Template +# +# See the MarkdownFeature class for setup details. + +describe 'GitLab Markdown' do + include Capybara::Node::Matchers + include MarkupHelper + include MarkdownMatchers + + # Sometimes it can be useful to see the parsed output of the Markdown document + # for debugging. Call this method to write the output to + # `tmp/capybara/.html`. + def write_markdown(filename = 'markdown_spec') + File.open(Rails.root.join("tmp/capybara/#{filename}.html"), 'w') do |file| + file.puts @html + end + end + + def doc(html = @html) + @doc ||= Nokogiri::HTML::DocumentFragment.parse(html) + end + + # Shared behavior that all pipelines should exhibit + shared_examples 'all pipelines' do + describe 'Redcarpet extensions' do + it 'does not parse emphasis inside of words' do + expect(doc.to_html).not_to match('foobarbaz') + end + + it 'parses table Markdown' do + aggregate_failures do + expect(doc).to have_selector('th:contains("Header")') + expect(doc).to have_selector('th:contains("Row")') + expect(doc).to have_selector('th:contains("Example")') + end + end + + it 'allows Markdown in tables' do + expect(doc.at_css('td:contains("Baz")').children.to_html) + .to eq 'Baz' + end + + it 'parses fenced code blocks' do + aggregate_failures do + expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.c') + expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.python') + end + end + + it 'parses mermaid code block' do + aggregate_failures do + expect(doc).to have_selector('pre[lang=mermaid] > code.js-render-mermaid') + end + end + + it 'parses strikethroughs' do + expect(doc).to have_selector(%{del:contains("and this text doesn't")}) + end + + it 'parses superscript' do + expect(doc).to have_selector('sup', count: 2) + end + end + + describe 'SanitizationFilter' do + it 'permits b elements' do + expect(doc).to have_selector('b:contains("b tag")') + end + + it 'permits em elements' do + expect(doc).to have_selector('em:contains("em tag")') + end + + it 'permits code elements' do + expect(doc).to have_selector('code:contains("code tag")') + end + + it 'permits kbd elements' do + expect(doc).to have_selector('kbd:contains("s")') + end + + it 'permits strike elements' do + expect(doc).to have_selector('strike:contains(Emoji)') + end + + it 'permits img elements' do + expect(doc).to have_selector('img[data-src*="smile.png"]') + end + + it 'permits br elements' do + expect(doc).to have_selector('br') + end + + it 'permits hr elements' do + expect(doc).to have_selector('hr') + end + + it 'permits span elements' do + expect(doc).to have_selector('span:contains("span tag")') + end + + it 'permits details elements' do + expect(doc).to have_selector('details:contains("Hiding the details")') + end + + it 'permits summary elements' do + expect(doc).to have_selector('details summary:contains("collapsible")') + end + + it 'permits style attribute in th elements' do + aggregate_failures do + expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center' + expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right' + expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left' + end + end + + it 'permits style attribute in td elements' do + aggregate_failures do + expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center' + expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right' + expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left' + end + end + + it 'removes `rel` attribute from links' do + expect(doc).not_to have_selector('a[rel="bookmark"]') + end + + it "removes `href` from `a` elements if it's fishy" do + expect(doc).not_to have_selector('a[href*="javascript"]') + end + end + + describe 'Escaping' do + it 'escapes non-tag angle brackets' do + table = doc.css('table').last.at_css('tbody') + expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 < 3 & 5' + end + end + + describe 'Edge Cases' do + it 'allows markup inside link elements' do + aggregate_failures do + expect(doc.at_css('a[href="#link-emphasis"]').to_html) + .to eq %{text} + + expect(doc.at_css('a[href="#link-strong"]').to_html) + .to eq %{text} + + expect(doc.at_css('a[href="#link-code"]').to_html) + .to eq %{text} + end + end + end + + describe 'ExternalLinkFilter' do + it 'adds nofollow to external link' do + link = doc.at_css('a:contains("Google")') + + expect(link.attr('rel')).to include('nofollow') + end + + it 'adds noreferrer to external link' do + link = doc.at_css('a:contains("Google")') + + expect(link.attr('rel')).to include('noreferrer') + end + + it 'adds _blank to target attribute for external links' do + link = doc.at_css('a:contains("Google")') + + expect(link.attr('target')).to match('_blank') + end + + it 'ignores internal link' do + link = doc.at_css('a:contains("GitLab Root")') + + expect(link.attr('rel')).not_to match 'nofollow' + expect(link.attr('target')).not_to match '_blank' + end + end + end + + before do + @feat = MarkdownFeature.new + + # `markdown` helper expects a `@project` and `@group` variable + @project = @feat.project + @group = @feat.group + end + + context 'default pipeline' do + before do + @html = markdown(@feat.raw_markdown) + end + + it_behaves_like 'all pipelines' + + it 'includes RelativeLinkFilter' do + expect(doc).to parse_relative_links + end + + it 'includes EmojiFilter' do + expect(doc).to parse_emoji + end + + it 'includes TableOfContentsFilter' do + expect(doc).to create_header_links + end + + it 'includes AutolinkFilter' do + expect(doc).to create_autolinks + end + + it 'includes all reference filters' do + aggregate_failures do + expect(doc).to reference_users + expect(doc).to reference_issues + expect(doc).to reference_merge_requests + expect(doc).to reference_snippets + expect(doc).to reference_commit_ranges + expect(doc).to reference_commits + expect(doc).to reference_labels + expect(doc).to reference_milestones + end + end + + it 'includes TaskListFilter' do + expect(doc).to parse_task_lists + end + + it 'includes InlineDiffFilter' do + expect(doc).to parse_inline_diffs + end + + it 'includes VideoLinkFilter' do + expect(doc).to parse_video_links + end + + it 'includes ColorFilter' do + expect(doc).to parse_colors + end + end + + context 'wiki pipeline' do + before do + @project_wiki = @feat.project_wiki + @project_wiki_page = @feat.project_wiki_page + + file = Gollum::File.new(@project_wiki.wiki) + expect(file).to receive(:path).and_return('images/example.jpg') + expect(@project_wiki).to receive(:find_file).with('images/example.jpg').and_return(file) + allow(@project_wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' } + + @html = markdown(@feat.raw_markdown, { pipeline: :wiki, project_wiki: @project_wiki, page_slug: @project_wiki_page.slug }) + end + + it_behaves_like 'all pipelines' + + it 'includes RelativeLinkFilter' do + expect(doc).not_to parse_relative_links + end + + it 'includes EmojiFilter' do + expect(doc).to parse_emoji + end + + it 'includes TableOfContentsFilter' do + expect(doc).to create_header_links + end + + it 'includes AutolinkFilter' do + expect(doc).to create_autolinks + end + + it 'includes all reference filters' do + aggregate_failures do + expect(doc).to reference_users + expect(doc).to reference_issues + expect(doc).to reference_merge_requests + expect(doc).to reference_snippets + expect(doc).to reference_commit_ranges + expect(doc).to reference_commits + expect(doc).to reference_labels + expect(doc).to reference_milestones + end + end + + it 'includes TaskListFilter' do + expect(doc).to parse_task_lists + end + + it 'includes GollumTagsFilter' do + expect(doc).to parse_gollum_tags + end + + it 'includes InlineDiffFilter' do + expect(doc).to parse_inline_diffs + end + + it 'includes VideoLinkFilter' do + expect(doc).to parse_video_links + end + + it 'includes ColorFilter' do + expect(doc).to parse_colors + end + end + + # Fake a `current_user` helper + def current_user + @feat.user + end +end diff --git a/spec/features/markdown/math_spec.rb b/spec/features/markdown/math_spec.rb new file mode 100644 index 00000000000..6a23d6b78ab --- /dev/null +++ b/spec/features/markdown/math_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe 'Math rendering', :js do + it 'renders inline and display math correctly' do + description = <<~MATH + This math is inline $`a^2+b^2=c^2`$. + + This is on a separate line + ```math + a^2+b^2=c^2 + ``` + MATH + + project = create(:project, :public) + issue = create(:issue, project: project, description: description) + + visit project_issue_path(project, issue) + + expect(page).to have_selector('.katex .mord.mathit', text: 'b') + expect(page).to have_selector('.katex-display .mord.mathit', text: 'b') + end +end diff --git a/spec/features/markdown/mermaid_spec.rb b/spec/features/markdown/mermaid_spec.rb new file mode 100644 index 00000000000..a25d701ee35 --- /dev/null +++ b/spec/features/markdown/mermaid_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe 'Mermaid rendering', :js do + it 'renders Mermaid diagrams correctly' do + description = <<~MERMAID + ```mermaid + graph TD; + A-->B; + A-->C; + B-->D; + C-->D; + ``` + MERMAID + + project = create(:project, :public) + issue = create(:issue, project: project, description: description) + + visit project_issue_path(project, issue) + + %w[A B C D].each do |label| + expect(page).to have_selector('svg foreignObject', text: label) + end + end +end diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb deleted file mode 100644 index f13d78d24e3..00000000000 --- a/spec/features/markdown_spec.rb +++ /dev/null @@ -1,337 +0,0 @@ -require 'spec_helper' -require 'erb' - -# This feature spec is intended to be a comprehensive exercising of all of -# GitLab's non-standard Markdown parsing and the integration thereof. -# -# These tests should be very high-level. Anything low-level belongs in the specs -# for the corresponding HTML::Pipeline filter or helper method. -# -# The idea is to pass a Markdown document through our entire processing stack. -# -# The process looks like this: -# -# Raw Markdown -# -> `markdown` helper -# -> Redcarpet::Render::GitlabHTML converts Markdown to HTML -# -> Post-process HTML -# -> `gfm` helper -# -> HTML::Pipeline -# -> SanitizationFilter -# -> Other filters, depending on pipeline -# -> `html_safe` -# -> Template -# -# See the MarkdownFeature class for setup details. - -describe 'GitLab Markdown' do - include Capybara::Node::Matchers - include MarkupHelper - include MarkdownMatchers - - # Sometimes it can be useful to see the parsed output of the Markdown document - # for debugging. Call this method to write the output to - # `tmp/capybara/.html`. - def write_markdown(filename = 'markdown_spec') - File.open(Rails.root.join("tmp/capybara/#{filename}.html"), 'w') do |file| - file.puts @html - end - end - - def doc(html = @html) - @doc ||= Nokogiri::HTML::DocumentFragment.parse(html) - end - - # Shared behavior that all pipelines should exhibit - shared_examples 'all pipelines' do - describe 'Redcarpet extensions' do - it 'does not parse emphasis inside of words' do - expect(doc.to_html).not_to match('foobarbaz') - end - - it 'parses table Markdown' do - aggregate_failures do - expect(doc).to have_selector('th:contains("Header")') - expect(doc).to have_selector('th:contains("Row")') - expect(doc).to have_selector('th:contains("Example")') - end - end - - it 'allows Markdown in tables' do - expect(doc.at_css('td:contains("Baz")').children.to_html) - .to eq 'Baz' - end - - it 'parses fenced code blocks' do - aggregate_failures do - expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.c') - expect(doc).to have_selector('pre.code.highlight.js-syntax-highlight.python') - end - end - - it 'parses mermaid code block' do - aggregate_failures do - expect(doc).to have_selector('pre[lang=mermaid] > code.js-render-mermaid') - end - end - - it 'parses strikethroughs' do - expect(doc).to have_selector(%{del:contains("and this text doesn't")}) - end - - it 'parses superscript' do - expect(doc).to have_selector('sup', count: 2) - end - end - - describe 'SanitizationFilter' do - it 'permits b elements' do - expect(doc).to have_selector('b:contains("b tag")') - end - - it 'permits em elements' do - expect(doc).to have_selector('em:contains("em tag")') - end - - it 'permits code elements' do - expect(doc).to have_selector('code:contains("code tag")') - end - - it 'permits kbd elements' do - expect(doc).to have_selector('kbd:contains("s")') - end - - it 'permits strike elements' do - expect(doc).to have_selector('strike:contains(Emoji)') - end - - it 'permits img elements' do - expect(doc).to have_selector('img[data-src*="smile.png"]') - end - - it 'permits br elements' do - expect(doc).to have_selector('br') - end - - it 'permits hr elements' do - expect(doc).to have_selector('hr') - end - - it 'permits span elements' do - expect(doc).to have_selector('span:contains("span tag")') - end - - it 'permits details elements' do - expect(doc).to have_selector('details:contains("Hiding the details")') - end - - it 'permits summary elements' do - expect(doc).to have_selector('details summary:contains("collapsible")') - end - - it 'permits style attribute in th elements' do - aggregate_failures do - expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center' - expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right' - expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left' - end - end - - it 'permits style attribute in td elements' do - aggregate_failures do - expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center' - expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right' - expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left' - end - end - - it 'removes `rel` attribute from links' do - expect(doc).not_to have_selector('a[rel="bookmark"]') - end - - it "removes `href` from `a` elements if it's fishy" do - expect(doc).not_to have_selector('a[href*="javascript"]') - end - end - - describe 'Escaping' do - it 'escapes non-tag angle brackets' do - table = doc.css('table').last.at_css('tbody') - expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 < 3 & 5' - end - end - - describe 'Edge Cases' do - it 'allows markup inside link elements' do - aggregate_failures do - expect(doc.at_css('a[href="#link-emphasis"]').to_html) - .to eq %{text} - - expect(doc.at_css('a[href="#link-strong"]').to_html) - .to eq %{text} - - expect(doc.at_css('a[href="#link-code"]').to_html) - .to eq %{text} - end - end - end - - describe 'ExternalLinkFilter' do - it 'adds nofollow to external link' do - link = doc.at_css('a:contains("Google")') - - expect(link.attr('rel')).to include('nofollow') - end - - it 'adds noreferrer to external link' do - link = doc.at_css('a:contains("Google")') - - expect(link.attr('rel')).to include('noreferrer') - end - - it 'adds _blank to target attribute for external links' do - link = doc.at_css('a:contains("Google")') - - expect(link.attr('target')).to match('_blank') - end - - it 'ignores internal link' do - link = doc.at_css('a:contains("GitLab Root")') - - expect(link.attr('rel')).not_to match 'nofollow' - expect(link.attr('target')).not_to match '_blank' - end - end - end - - before do - @feat = MarkdownFeature.new - - # `markdown` helper expects a `@project` and `@group` variable - @project = @feat.project - @group = @feat.group - end - - context 'default pipeline' do - before do - @html = markdown(@feat.raw_markdown) - end - - it_behaves_like 'all pipelines' - - it 'includes RelativeLinkFilter' do - expect(doc).to parse_relative_links - end - - it 'includes EmojiFilter' do - expect(doc).to parse_emoji - end - - it 'includes TableOfContentsFilter' do - expect(doc).to create_header_links - end - - it 'includes AutolinkFilter' do - expect(doc).to create_autolinks - end - - it 'includes all reference filters' do - aggregate_failures do - expect(doc).to reference_users - expect(doc).to reference_issues - expect(doc).to reference_merge_requests - expect(doc).to reference_snippets - expect(doc).to reference_commit_ranges - expect(doc).to reference_commits - expect(doc).to reference_labels - expect(doc).to reference_milestones - end - end - - it 'includes TaskListFilter' do - expect(doc).to parse_task_lists - end - - it 'includes InlineDiffFilter' do - expect(doc).to parse_inline_diffs - end - - it 'includes VideoLinkFilter' do - expect(doc).to parse_video_links - end - - it 'includes ColorFilter' do - expect(doc).to parse_colors - end - end - - context 'wiki pipeline' do - before do - @project_wiki = @feat.project_wiki - @project_wiki_page = @feat.project_wiki_page - - file = Gollum::File.new(@project_wiki.wiki) - expect(file).to receive(:path).and_return('images/example.jpg') - expect(@project_wiki).to receive(:find_file).with('images/example.jpg').and_return(file) - allow(@project_wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' } - - @html = markdown(@feat.raw_markdown, { pipeline: :wiki, project_wiki: @project_wiki, page_slug: @project_wiki_page.slug }) - end - - it_behaves_like 'all pipelines' - - it 'includes RelativeLinkFilter' do - expect(doc).not_to parse_relative_links - end - - it 'includes EmojiFilter' do - expect(doc).to parse_emoji - end - - it 'includes TableOfContentsFilter' do - expect(doc).to create_header_links - end - - it 'includes AutolinkFilter' do - expect(doc).to create_autolinks - end - - it 'includes all reference filters' do - aggregate_failures do - expect(doc).to reference_users - expect(doc).to reference_issues - expect(doc).to reference_merge_requests - expect(doc).to reference_snippets - expect(doc).to reference_commit_ranges - expect(doc).to reference_commits - expect(doc).to reference_labels - expect(doc).to reference_milestones - end - end - - it 'includes TaskListFilter' do - expect(doc).to parse_task_lists - end - - it 'includes GollumTagsFilter' do - expect(doc).to parse_gollum_tags - end - - it 'includes InlineDiffFilter' do - expect(doc).to parse_inline_diffs - end - - it 'includes VideoLinkFilter' do - expect(doc).to parse_video_links - end - - it 'includes ColorFilter' do - expect(doc).to parse_colors - end - end - - # Fake a `current_user` helper - def current_user - @feat.user - end -end -- cgit v1.2.1 From 5553524516135f573b1264785ef04f0fe638d5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 8 Feb 2018 18:04:52 +0100 Subject: Fix GCP cluster feature spec --- spec/features/projects/clusters/gcp_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'spec/features') diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index 02dbd3380b3..4d47cdb500c 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -25,7 +25,7 @@ feature 'Gcp Cluster', :js do context 'when user has a GCP project with billing enabled' do before do allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing) - allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return('true') + allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(true) end context 'when user does not have a cluster and visits cluster index page' do @@ -134,7 +134,7 @@ feature 'Gcp Cluster', :js do context 'when user does not have a GCP project with billing enabled' do before do allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing) - allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return('false') + allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(false) visit project_clusters_path(project) -- cgit v1.2.1 From 499d0501854226c018936a56f02e98bd00988742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarka=20Kadlecov=C3=A1?= Date: Fri, 9 Feb 2018 11:18:53 +0100 Subject: Display a link to external issue tracker when enabled --- .../services/user_activates_issue_tracker_spec.rb | 92 ++++++++++++++++++++++ .../projects/services/user_activates_jira_spec.rb | 31 +++++--- 2 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 spec/features/projects/services/user_activates_issue_tracker_spec.rb (limited to 'spec/features') diff --git a/spec/features/projects/services/user_activates_issue_tracker_spec.rb b/spec/features/projects/services/user_activates_issue_tracker_spec.rb new file mode 100644 index 00000000000..e9502178bd7 --- /dev/null +++ b/spec/features/projects/services/user_activates_issue_tracker_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper' + +describe 'User activates issue tracker', :js do + let(:user) { create(:user) } + let(:project) { create(:project) } + + let(:url) { 'http://tracker.example.com' } + + def fill_form(active = true) + check 'Active' if active + + fill_in 'service_project_url', with: url + fill_in 'service_issues_url', with: "#{url}/:id" + fill_in 'service_new_issue_url', with: url + end + + before do + project.add_master(user) + sign_in(user) + + visit project_settings_integrations_path(project) + end + + shared_examples 'external issue tracker activation' do |tracker:| + describe 'user sets and activates the Service' do + context 'when the connection test succeeds' do + before do + stub_request(:head, url).to_return(headers: { 'Content-Type' => 'application/json' }) + + click_link(tracker) + fill_form + click_button('Test settings and save changes') + wait_for_requests + end + + it 'activates the service' do + expect(page).to have_content("#{tracker} activated.") + expect(current_path).to eq(project_settings_integrations_path(project)) + end + + it 'shows the link in the menu' do + page.within('.nav-sidebar') do + expect(page).to have_link(tracker, href: url) + end + end + end + + context 'when the connection test fails' do + it 'activates the service' do + stub_request(:head, url).to_raise(HTTParty::Error) + + click_link(tracker) + fill_form + click_button('Test settings and save changes') + wait_for_requests + + expect(find('.flash-container-page')).to have_content 'Test failed.' + expect(find('.flash-container-page')).to have_content 'Save anyway' + + find('.flash-alert .flash-action').click + wait_for_requests + + expect(page).to have_content("#{tracker} activated.") + expect(current_path).to eq(project_settings_integrations_path(project)) + end + end + end + + describe 'user sets the service but keeps it disabled' do + before do + click_link(tracker) + fill_form(false) + click_button('Save changes') + end + + it 'saves but does not activate the service' do + expect(page).to have_content("#{tracker} settings saved, but not activated.") + expect(current_path).to eq(project_settings_integrations_path(project)) + end + + it 'does not show the external tracker link in the menu' do + page.within('.nav-sidebar') do + expect(page).not_to have_link(tracker, href: url) + end + end + end + end + + it_behaves_like 'external issue tracker activation', tracker: 'Redmine' + it_behaves_like 'external issue tracker activation', tracker: 'Bugzilla' + it_behaves_like 'external issue tracker activation', tracker: 'Custom Issue Tracker' +end diff --git a/spec/features/projects/services/user_activates_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb index 028669eeaf2..429128ec096 100644 --- a/spec/features/projects/services/user_activates_jira_spec.rb +++ b/spec/features/projects/services/user_activates_jira_spec.rb @@ -3,7 +3,6 @@ require 'spec_helper' describe 'User activates Jira', :js do let(:user) { create(:user) } let(:project) { create(:project) } - let(:service) { project.create_jira_service } let(:url) { 'http://jira.example.com' } let(:test_url) { 'http://jira.example.com/rest/api/2/serverInfo' } @@ -26,7 +25,7 @@ describe 'User activates Jira', :js do describe 'user sets and activates Jira Service' do context 'when Jira connection test succeeds' do - it 'activates the JIRA service' do + before do server_info = { key: 'value' }.to_json WebMock.stub_request(:get, test_url).with(basic_auth: %w(username password)).to_return(body: server_info) @@ -34,10 +33,18 @@ describe 'User activates Jira', :js do fill_form click_button('Test settings and save changes') wait_for_requests + end + it 'activates the JIRA service' do expect(page).to have_content('JIRA activated.') expect(current_path).to eq(project_settings_integrations_path(project)) end + + it 'shows the JIRA link in the menu' do + page.within('.nav-sidebar') do + expect(page).to have_link('JIRA', href: url) + end + end end context 'when Jira connection test fails' do @@ -75,14 +82,20 @@ describe 'User activates Jira', :js do end describe 'user sets Jira Service but keeps it disabled' do - context 'when Jira connection test succeeds' do - it 'activates the JIRA service' do - click_link('JIRA') - fill_form(false) - click_button('Save changes') + before do + click_link('JIRA') + fill_form(false) + click_button('Save changes') + end - expect(page).to have_content('JIRA settings saved, but not activated.') - expect(current_path).to eq(project_settings_integrations_path(project)) + it 'saves but does not activate the JIRA service' do + expect(page).to have_content('JIRA settings saved, but not activated.') + expect(current_path).to eq(project_settings_integrations_path(project)) + end + + it 'does not show the JIRA link in the menu' do + page.within('.nav-sidebar') do + expect(page).not_to have_link('JIRA', href: url) end end end -- cgit v1.2.1 From 5b5557c67c43387c02345ecf0636bfcf9ef8ec0d Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Sun, 11 Feb 2018 12:16:35 +0500 Subject: Move feature group members search test to RSpec https://gitlab.com/gitlab-org/gitlab-ce/issues/23036 --- .../features/groups/members/search_members_spec.rb | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 spec/features/groups/members/search_members_spec.rb (limited to 'spec/features') diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb new file mode 100644 index 00000000000..31fbbcf562c --- /dev/null +++ b/spec/features/groups/members/search_members_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe 'Search group member' do + let(:user) { create :user } + let(:member) { create :user } + + let!(:guest_group) do + create(:group) do |group| + group.add_guest(user) + group.add_guest(member) + end + end + + before do + sign_in(user) + visit group_group_members_path(guest_group) + end + + it 'renders member users' do + page.within '.member-search-form' do + fill_in 'search', with: member.name + find('.member-search-btn').click + end + + group_members_list = find(".panel .content-list") + expect(group_members_list).to have_content(member.name) + expect(group_members_list).not_to have_content(user.name) + end +end -- cgit v1.2.1 From b7f5205ab136f4835006533282002a075bf9053a Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Tue, 13 Feb 2018 00:17:19 +0500 Subject: Move spinach profile tests to RSpec https://gitlab.com/gitlab-org/gitlab-ce/issues/23036 --- spec/features/profiles/password_spec.rb | 76 ++++++++++++++++++++-- spec/features/profiles/user_edit_profile_spec.rb | 58 +++++++++++++++++ .../profiles/user_manages_applications_spec.rb | 39 +++++++++++ .../user_visits_profile_authentication_log_spec.rb | 25 +++++-- spec/features/profiles/user_visits_profile_spec.rb | 52 +++++++++++++-- 5 files changed, 231 insertions(+), 19 deletions(-) create mode 100644 spec/features/profiles/user_edit_profile_spec.rb create mode 100644 spec/features/profiles/user_manages_applications_spec.rb (limited to 'spec/features') diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb index 4665626f114..1d7700b6767 100644 --- a/spec/features/profiles/password_spec.rb +++ b/spec/features/profiles/password_spec.rb @@ -1,6 +1,15 @@ require 'spec_helper' describe 'Profile > Password' do + let(:user) { create(:user) } + + def fill_passwords(password, confirmation) + fill_in 'New password', with: password + fill_in 'Password confirmation', with: confirmation + + click_button 'Save password' + end + context 'Password authentication enabled' do let(:user) { create(:user, password_automatically_set: true) } @@ -9,13 +18,6 @@ describe 'Profile > Password' do visit edit_profile_password_path end - def fill_passwords(password, confirmation) - fill_in 'New password', with: password - fill_in 'Password confirmation', with: confirmation - - click_button 'Save password' - end - context 'User with password automatically set' do describe 'User puts different passwords in the field and in the confirmation' do it 'shows an error message' do @@ -73,4 +75,64 @@ describe 'Profile > Password' do end end end + + context 'Change passowrd' do + before do + sign_in(user) + visit(edit_profile_password_path) + end + + it 'does not change user passowrd without old one' do + page.within '.update-password' do + fill_passwords('22233344', '22233344') + end + + page.within '.flash-container' do + expect(page).to have_content 'You must provide a valid current password' + end + end + + it 'does not change password with invalid old password' do + page.within '.update-password' do + fill_in 'user_current_password', with: 'invalid' + fill_passwords('password', 'confirmation') + end + + page.within '.flash-container' do + expect(page).to have_content 'You must provide a valid current password' + end + end + + it 'changes user password' do + page.within '.update-password' do + fill_in "user_current_password", with: user.password + fill_passwords('22233344', '22233344') + end + + expect(current_path).to eq new_user_session_path + end + end + + context 'when password is expired' do + before do + sign_in(user) + + user.update_attributes(password_expires_at: 1.hour.ago) + user.identities.delete + expect(user.ldap_user?).to eq false + end + + it 'needs change user password' do + visit edit_profile_password_path + + expect(current_path).to eq new_profile_password_path + + fill_in :user_current_password, with: user.password + fill_in :user_password, with: '12345678' + fill_in :user_password_confirmation, with: '12345678' + click_button 'Set new password' + + expect(current_path).to eq new_user_session_path + end + end end diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb new file mode 100644 index 00000000000..0b5eacbe916 --- /dev/null +++ b/spec/features/profiles/user_edit_profile_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe 'User edit profile' do + let(:user) { create(:user) } + + before do + sign_in(user) + visit(profile_path) + end + + it 'changes user profile' do + fill_in 'user_skype', with: 'testskype' + fill_in 'user_linkedin', with: 'testlinkedin' + fill_in 'user_twitter', with: 'testtwitter' + fill_in 'user_website_url', with: 'testurl' + fill_in 'user_location', with: 'Ukraine' + fill_in 'user_bio', with: 'I <3 GitLab' + fill_in 'user_organization', with: 'GitLab' + click_button 'Update profile settings' + + expect(user.reload).to have_attributes( + skype: 'testskype', + linkedin: 'testlinkedin', + twitter: 'testtwitter', + website_url: 'testurl', + bio: 'I <3 GitLab', + organization: 'GitLab' + ) + + expect(find('#user_location').value).to eq 'Ukraine' + expect(page).to have_content('Profile was successfully updated') + end + + context 'user avatar' do + before do + attach_file(:user_avatar, Rails.root.join('spec', 'fixtures', 'banana_sample.gif')) + click_button 'Update profile settings' + end + + it 'changes user avatar' do + expect(page).to have_link('Remove avatar') + + user.reload + expect(user.avatar).to be_instance_of AvatarUploader + expect(user.avatar.url).to eq "/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif" + end + + it 'removes user avatar' do + click_link 'Remove avatar' + + user.reload + + expect(user.avatar?).to eq false + expect(page).not_to have_link('Remove avatar') + expect(page).to have_link('gravatar.com') + end + end +end diff --git a/spec/features/profiles/user_manages_applications_spec.rb b/spec/features/profiles/user_manages_applications_spec.rb new file mode 100644 index 00000000000..387584fef62 --- /dev/null +++ b/spec/features/profiles/user_manages_applications_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'User manages applications' do + let(:user) { create(:user) } + + before do + sign_in(user) + visit applications_profile_path + end + + it 'manages applications' do + expect(page).to have_content 'Add new application' + + fill_in :doorkeeper_application_name, with: 'test' + fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com' + click_on 'Save application' + + expect(page).to have_content 'Application: test' + expect(page).to have_content 'Application Id' + expect(page).to have_content 'Secret' + + click_on 'Edit' + + expect(page).to have_content 'Edit application' + fill_in :doorkeeper_application_name, with: 'test_changed' + click_on 'Save application' + + expect(page).to have_content 'test_changed' + expect(page).to have_content 'Application Id' + expect(page).to have_content 'Secret' + + visit applications_profile_path + + page.within '.oauth-applications' do + click_on 'Destroy' + end + expect(page.find('.oauth-applications')).not_to have_content 'test_changed' + end +end diff --git a/spec/features/profiles/user_visits_profile_authentication_log_spec.rb b/spec/features/profiles/user_visits_profile_authentication_log_spec.rb index a50ebb29e01..0f419c3c2c0 100644 --- a/spec/features/profiles/user_visits_profile_authentication_log_spec.rb +++ b/spec/features/profiles/user_visits_profile_authentication_log_spec.rb @@ -3,13 +3,28 @@ require 'spec_helper' describe 'User visits the authentication log' do let(:user) { create(:user) } - before do - sign_in(user) + context 'when user signed in' do + before do + sign_in(user) + end - visit(audit_log_profile_path) + it 'shows correct menu item' do + visit(audit_log_profile_path) + + expect(page).to have_active_navigation('Authentication log') + end end - it 'shows correct menu item' do - expect(page).to have_active_navigation('Authentication log') + context 'when user has activity' do + before do + create(:closed_issue_event, author: user) + gitlab_sign_in(user) + end + + it 'shows user activity' do + visit(audit_log_profile_path) + + expect(page).to have_content 'Signed in with standard authentication' + end end end diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb index a5d80439143..713112477c8 100644 --- a/spec/features/profiles/user_visits_profile_spec.rb +++ b/spec/features/profiles/user_visits_profile_spec.rb @@ -5,20 +5,58 @@ describe 'User visits their profile' do before do sign_in(user) - - visit(profile_path) end it 'shows correct menu item' do + visit(profile_path) + expect(page).to have_active_navigation('Profile') end - describe 'profile settings', :js do - it 'saves updates' do - fill_in 'user_bio', with: 'bio' - click_button 'Update profile settings' + it 'shows profile info' do + visit(profile_path) + + expect(page).to have_content "This information will appear on your profile" + end + + context 'when user has groups' do + let(:group) do + create :group do |group| + group.add_owner(user) + end + end + + let!(:project) do + create(:project, :repository, namespace: group) do |project| + create(:closed_issue_event, project: project) + project.add_master(user) + end + end + + def click_on_profile_picture + find(:css, '.header-user-dropdown-toggle').click + + page.within ".header-user" do + click_link "Profile" + end + end + + it 'shows user groups', :js do + visit(profile_path) + click_on_profile_picture + + page.within ".cover-block" do + expect(page).to have_content user.name + expect(page).to have_content user.username + end + + page.within ".content" do + click_link "Groups" + end - expect(page).to have_content('Profile was successfully updated') + page.within "#groups" do + expect(page).to have_content group.name + end end end end -- cgit v1.2.1 From 44ec3db42687115e94036f3f480ccba1c90de740 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Thu, 15 Feb 2018 08:26:51 +0000 Subject: Move spinach group milestones test to RSpec --- spec/features/groups/milestone_spec.rb | 151 +++++++++++++++++++++++++-------- 1 file changed, 114 insertions(+), 37 deletions(-) (limited to 'spec/features') diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 1b41b3842c8..20337f1d3b0 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Group milestones', :js do +feature 'Group milestones' do let(:group) { create(:group) } let!(:project) { create(:project_empty_repo, group: group) } let(:user) { create(:group_member, :master, user: create(:user), group: group ).user } @@ -13,7 +13,7 @@ feature 'Group milestones', :js do sign_in(user) end - context 'create a milestone' do + context 'create a milestone', :js do before do visit new_group_milestone_path(group) end @@ -61,55 +61,132 @@ feature 'Group milestones', :js do end context 'milestones list' do - let!(:other_project) { create(:project_empty_repo, group: group) } - - let!(:active_project_milestone1) { create(:milestone, project: project, state: 'active', title: 'v1.0') } - let!(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.0') } - let!(:closed_project_milestone1) { create(:milestone, project: project, state: 'closed', title: 'v2.0') } - let!(:closed_project_milestone2) { create(:milestone, project: other_project, state: 'closed', title: 'v2.0') } - let!(:active_group_milestone) { create(:milestone, group: group, state: 'active') } - let!(:closed_group_milestone) { create(:milestone, group: group, state: 'closed') } - - before do - visit group_milestones_path(group) + context 'when no milestones' do + it 'renders no milestones text' do + visit group_milestones_path(group) + expect(page).to have_content('No milestones to show') + end end - it 'counts milestones correctly' do - expect(find('.top-area .active .badge').text).to eq("2") - expect(find('.top-area .closed .badge').text).to eq("2") - expect(find('.top-area .all .badge').text).to eq("4") - end + context 'when milestones exists' do + let!(:other_project) { create(:project_empty_repo, group: group) } + + let!(:active_project_milestone1) do + create( + :milestone, + project: project, + state: 'active', + title: 'v1.0', + due_date: '2114-08-20', + description: 'Lorem Ipsum is simply dummy text' + ) + end + let!(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.0') } + let!(:closed_project_milestone1) { create(:milestone, project: project, state: 'closed', title: 'v2.0') } + let!(:closed_project_milestone2) { create(:milestone, project: other_project, state: 'closed', title: 'v2.0') } + let!(:active_group_milestone) { create(:milestone, group: group, state: 'active', title: 'GL-113') } + let!(:closed_group_milestone) { create(:milestone, group: group, state: 'closed') } + let!(:issue) do + create :issue, project: project, assignees: [user], author: user, milestone: active_project_milestone1 + end - it 'lists legacy group milestones and group milestones' do - legacy_milestone = GroupMilestone.build_collection(group, group.projects, { state: 'active' }).first + before do + visit group_milestones_path(group) + end - expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1) - expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1) - end + it 'counts milestones correctly' do + expect(find('.top-area .active .badge').text).to eq("2") + expect(find('.top-area .closed .badge').text).to eq("2") + expect(find('.top-area .all .badge').text).to eq("4") + end - it 'updates milestone' do - page.within(".milestones #milestone_#{active_group_milestone.id}") do - click_link('Edit') + it 'lists legacy group milestones and group milestones' do + legacy_milestone = GroupMilestone.build_collection(group, group.projects, { state: 'active' }).first + + expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1) + expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1) end - page.within('.milestone-form') do - fill_in 'milestone_title', with: 'new title' - click_button('Update milestone') + it 'updates milestone' do + page.within(".milestones #milestone_#{active_group_milestone.id}") do + click_link('Edit') + end + + page.within('.milestone-form') do + fill_in 'milestone_title', with: 'new title' + click_button('Update milestone') + end + + expect(find('#content-body h2')).to have_content('new title') end - expect(find('#content-body h2')).to have_content('new title') - end + it 'shows milestone detail and supports its edit' do + page.within(".milestones #milestone_#{active_group_milestone.id}") do + click_link(active_group_milestone.title) + end + + page.within('.detail-page-header') do + click_link('Edit') + end - it 'shows milestone detail and supports its edit' do - page.within(".milestones #milestone_#{active_group_milestone.id}") do - click_link(active_group_milestone.title) + expect(page).to have_selector('.milestone-form') end - page.within('.detail-page-header') do - click_link('Edit') + it 'renders milestones' do + expect(page).to have_content('v1.0') + expect(page).to have_content('GL-113') + expect(page).to have_link( + '1 Issue', + href: issues_group_path(group, milestone_title: 'v1.0') + ) + expect(page).to have_link( + '0 Merge Requests', + href: merge_requests_group_path(group, milestone_title: 'v1.0') + ) end - expect(page).to have_selector('.milestone-form') + it 'renders group milestone details' do + click_link 'v1.0' + + expect(page).to have_content('expires on Aug 20, 2114') + expect(page).to have_content('v1.0') + expect(page).to have_content('Issues 1 Open: 1 Closed: 0') + expect(page).to have_link(issue.title, href: project_issue_path(issue.project, issue)) + end + + describe 'labels' do + before do + create(:label, project: project, title: 'bug') do |label| + issue.labels << label + end + + create(:label, project: project, title: 'feature') do |label| + issue.labels << label + end + end + + it 'renders labels' do + click_link 'v1.0' + + page.within('#tab-issues') do + expect(page).to have_content 'bug' + expect(page).to have_content 'feature' + end + end + + it 'renders labels list', :js do + click_link 'v1.0' + + page.within('.content .nav-links') do + page.find(:xpath, "//a[@href='#tab-labels']").click + end + + page.within('#tab-labels') do + expect(page).to have_content 'bug' + expect(page).to have_content 'feature' + end + end + end end end end -- cgit v1.2.1