diff --git a/app/views/account/_login.html.erb b/app/views/account/_login.html.erb index 112f59476a0..f14f204318f 100644 --- a/app/views/account/_login.html.erb +++ b/app/views/account/_login.html.erb @@ -77,7 +77,7 @@ See COPYRIGHT and LICENSE files for more details.   + value="<%=t(:button_login)%>" class="button -primary button_no-margin" tabindex="1" /> diff --git a/app/views/account/_password_login_form.html.erb b/app/views/account/_password_login_form.html.erb index 1fcf9d217bd..ed3d7816f88 100644 --- a/app/views/account/_password_login_form.html.erb +++ b/app/views/account/_password_login_form.html.erb @@ -57,21 +57,24 @@ See COPYRIGHT and LICENSE files for more details. <%= submit_tag t(:button_login), name: :login, - class: 'button -primary', + class: 'button -primary button_no-margin', data: { disable_with: t(:label_loading) } %> -
- <% end %> diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index 4b0de20a7ed..156563d6e09 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -384,17 +384,20 @@ en: learn_about: "Learn more about the new features" # Include the version to invalidate outdated translations in other locales. # Otherwise, e.g. chinese might still have the translations for 10.0 in the 12.0 release. - "13_3": + "13_4": standard: - learn_about_link: https://www.openproject.org/blog/openproject-13-3-release/ + learn_about_link: https://www.openproject.org/blog/openproject-13-4-release/ new_features_html: > The release contains various new features and improvements:
ical_sharing_modal: diff --git a/docker/pullpreview/docker-compose.yml b/docker/pullpreview/docker-compose.yml index f4879929d17..7c9664a6063 100644 --- a/docker/pullpreview/docker-compose.yml +++ b/docker/pullpreview/docker-compose.yml @@ -45,7 +45,7 @@ services: - "caddy_data:/data" db: - image: postgres:10 + image: postgres:13 environment: POSTGRES_USER: app POSTGRES_PASSWORD: p4ssw0rd diff --git a/frontend/src/app/core/global_search/input/global-search-input.component.html b/frontend/src/app/core/global_search/input/global-search-input.component.html index 13f26dd12eb..61491b30798 100644 --- a/frontend/src/app/core/global_search/input/global-search-input.component.html +++ b/frontend/src/app/core/global_search/input/global-search-input.component.html @@ -26,11 +26,12 @@ [searchFn]="customSearchFn" [virtualScroll]="false" (focus)="onFocus()" + (blur)="onFocusOut()" (search)="search($event)" (close)="onClose()" (change)="followItem($event)" (keydown.enter)="onEnterBeforeResultsLoaded()" - (keydown.escape)="blur()" + (keydown.escape)="onFocusOut()" (clear)="clearSearch()" [filters]="autocompleterOptions.filters" [resource]="autocompleterOptions.resource" diff --git a/frontend/src/app/core/global_search/input/global-search-input.component.ts b/frontend/src/app/core/global_search/input/global-search-input.component.ts index cebe005a1e2..aa2e8291087 100644 --- a/frontend/src/app/core/global_search/input/global-search-input.component.ts +++ b/frontend/src/app/core/global_search/input/global-search-input.component.ts @@ -251,6 +251,8 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { this.selectedItem = undefined; this.toggleTopMenuClass(); } + + (document.activeElement).blur(); } public onClose():void { @@ -467,11 +469,6 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { } } - public blur():void { - this.ngSelectComponent.ngSelectInstance.searchTerm = ''; - (document.activeElement).blur(); - } - private get currentScope():string { const serviceScope = this.globalSearchService.projectScope; return (serviceScope === '') ? 'current_project_and_all_descendants' : serviceScope; diff --git a/frontend/src/app/features/homescreen/blocks/new-features.component.ts b/frontend/src/app/features/homescreen/blocks/new-features.component.ts index 37241f6597d..b6f044b4fdd 100644 --- a/frontend/src/app/features/homescreen/blocks/new-features.component.ts +++ b/frontend/src/app/features/homescreen/blocks/new-features.component.ts @@ -35,10 +35,10 @@ import { imagePath } from 'core-app/shared/helpers/images/path-helper'; export const homescreenNewFeaturesBlockSelector = 'homescreen-new-features-block'; // The key used in the I18n files to distinguish between versions. -const OpVersionI18n = '13_3'; +const OpVersionI18n = '13_4'; /** Update the teaser image to the next version */ -const featureTeaserImage = '13_3_features.svg'; +const featureTeaserImage = '13_4_features.svg'; @Component({ template: ` diff --git a/frontend/src/app/features/team-planner/team-planner/assignee/add-assignee.component.ts b/frontend/src/app/features/team-planner/team-planner/assignee/add-assignee.component.ts index 435b8e463f3..9d6fefe8fc3 100644 --- a/frontend/src/app/features/team-planner/team-planner/assignee/add-assignee.component.ts +++ b/frontend/src/app/features/team-planner/team-planner/assignee/add-assignee.component.ts @@ -85,9 +85,9 @@ export class AddAssigneeComponent { .apiV3Service .principals .filtered(filters) - .get() + .getPaginatedResults() .pipe( - map((collection) => collection.elements.filter( + map((elements) => elements.filter( (user) => !this.alreadySelected.find((selected) => selected === user.id), )), ); diff --git a/frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts b/frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts index 2755041a09e..75342254a4d 100644 --- a/frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts +++ b/frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts @@ -82,6 +82,7 @@ import { import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { MAGIC_PAGE_NUMBER } from 'core-app/core/apiv3/helpers/get-paginated-results'; import { CalendarDragDropService } from 'core-app/features/team-planner/team-planner/calendar-drag-drop.service'; import { StatusResource } from 'core-app/features/hal/resources/status-resource'; import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; @@ -223,7 +224,7 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit, return this .capabilitiesResourceService - .fetchCapabilities({ pageSize: -1, filters }); + .fetchCapabilities({ pageSize: MAGIC_PAGE_NUMBER, filters }); }), map((result) => result ._embedded @@ -250,6 +251,7 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit, filter((ids) => ids.length > 0), map((ids) => ({ filters: [['id', '=', ids]], + pageSize: MAGIC_PAGE_NUMBER, }) as ApiV3ListParameters), ); diff --git a/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.html b/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.html index afb48aa709d..61802814662 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.html +++ b/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.html @@ -8,4 +8,13 @@ - + +
+ +
+ + +
diff --git a/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.ts b/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.ts index 31c9b9f068f..64377300b05 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/wp-calendar/wp-calendar.component.ts @@ -33,6 +33,7 @@ import { CurrentProjectService } from 'core-app/core/current-project/current-pro import { WorkPackageIsolatedQuerySpaceDirective, } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -40,10 +41,19 @@ import { hostDirectives: [WorkPackageIsolatedQuerySpaceDirective], }) export class WidgetWpCalendarComponent extends AbstractWidgetComponent { - constructor(protected readonly i18n:I18nService, + text = { + missing_permission: this.I18n.t('js.grid.widgets.missing_permission'), + }; + + hasCapability$ = this.currentUser.hasCapabilities$('work_packages/read', this.currentProject.id); + + constructor( + protected readonly I18n:I18nService, protected readonly injector:Injector, - protected readonly currentProject:CurrentProjectService) { - super(i18n, injector); + protected readonly currentProject:CurrentProjectService, + protected readonly currentUser:CurrentUserService, + ) { + super(I18n, injector); } public get projectIdentifier() { diff --git a/frontend/src/app/shared/components/storages/file-link-list-item/file-link-list-item.component.ts b/frontend/src/app/shared/components/storages/file-link-list-item/file-link-list-item.component.ts index 41d12a412d2..1b0c0c344e9 100644 --- a/frontend/src/app/shared/components/storages/file-link-list-item/file-link-list-item.component.ts +++ b/frontend/src/app/shared/components/storages/file-link-list-item/file-link-list-item.component.ts @@ -33,8 +33,10 @@ import { ElementRef, EventEmitter, Input, + OnChanges, OnInit, Output, + SimpleChanges, ViewChild, } from '@angular/core'; @@ -60,7 +62,7 @@ import SpotDropAlignmentOption from 'core-app/spot/drop-alignment-options'; templateUrl: './file-link-list-item.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class FileLinkListItemComponent implements OnInit, AfterViewInit { +export class FileLinkListItemComponent implements OnInit, OnChanges, AfterViewInit { @Input() public fileLink:IFileLink; @Input() public allowEditing = false; @@ -132,8 +134,16 @@ export class FileLinkListItemComponent implements OnInit, AfterViewInit { 'js.storages.file_links.download', { fileName: this.fileLink.originData.name }, ); + } - this.floatingActions = this.getFloatingActions(); + // Before, the getFloatingActions() method was called in the ngOnInit() method. + // The value of the allowEditing property can be calculated after the component is already initialized (in fact it is determined + // asynchronously, by getting a value from the server in a separate request). Therefore, the available actions need + // to be calculated whenever the value is set. + ngOnChanges(changes:SimpleChanges):void { + if (changes.allowEditing) { + this.floatingActions = this.getFloatingActions(); + } } ngAfterViewInit():void { diff --git a/frontend/src/assets/images/13_3_features.svg b/frontend/src/assets/images/13_3_features.svg deleted file mode 100644 index 620ebdbccf8..00000000000 --- a/frontend/src/assets/images/13_3_features.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/assets/images/13_4_features.svg b/frontend/src/assets/images/13_4_features.svg new file mode 100644 index 00000000000..4a61bf1212c --- /dev/null +++ b/frontend/src/assets/images/13_4_features.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/global_styles/content/_accounts.sass b/frontend/src/global_styles/content/_accounts.sass index d6e0635ab88..e25743fcc01 100644 --- a/frontend/src/global_styles/content/_accounts.sass +++ b/frontend/src/global_styles/content/_accounts.sass @@ -28,16 +28,14 @@ #login-form margin: 50px auto 0 - padding-top: 20px + padding: 20px width: 511px word-break: break-word - .login-options-container + .login-form--footer + display: flex + justify-content: space-between margin-bottom: 10px - .login-links - float: right - text-align: right - margin-top: -3rem #content .login-auth-providers.wide width: auto @@ -63,13 +61,9 @@ margin-top: 25px h3 - border: none - margin-top: 20px font-weight: normal font-size: 1rem - text-decoration: none text-align: center - position: relative z-index: 1 @@ -101,24 +95,11 @@ @include prevent-float-collapse margin-top: 1em - a.auth-provider - float: left - margin-left: 10px - background-color: light-grey - font-weight: normal - color: var(--body-font-color) - - &:hover - text-decoration: none - - .auth-provider-name - line-height: normal - white-space: normal + a.auth-provider:hover + text-decoration: none #nav-login-content .login-auth-providers h3 - &:before - width: 100% span background: var(--header-drop-down-bg-color) .login-auth-provider-list diff --git a/frontend/src/global_styles/content/_accounts_mobile.sass b/frontend/src/global_styles/content/_accounts_mobile.sass index 98a4a4ae8e4..0d3f10e4b07 100644 --- a/frontend/src/global_styles/content/_accounts_mobile.sass +++ b/frontend/src/global_styles/content/_accounts_mobile.sass @@ -32,31 +32,23 @@ #nav-login-content .login-auth-providers width: 100% - #content .login-auth-providers - margin-top: 114px - - h3:before - top: -14px - #login-form .form--field-container @include grid-content(12) padding: 0 + margin: 0 - .login-options-container - position: relative + .login-form--footer + flex-direction: column + align-items: center - .login-links - float: none - position: absolute - text-align: center - width: 100% - margin-top: 0 - - a - display: block + .login-form--footer-link + display: inline-block + margin-top: 15px .button + margin-right: 0 + margin-left: 0 width: 100% #new_user diff --git a/frontend/src/global_styles/content/modules/_2fa.sass b/frontend/src/global_styles/content/modules/_2fa.sass index 552cc8a8b0c..bc015422b9b 100644 --- a/frontend/src/global_styles/content/modules/_2fa.sass +++ b/frontend/src/global_styles/content/modules/_2fa.sass @@ -54,42 +54,14 @@ .mobile-otp-new-device--body flex: 1 -#login-form input[type=radio] - float: left - #resend_otp_container margin-top: 30px -#resend_otp_container input[type=radio] - margin-right: 8px - -#resend_otp_container label - margin-right: 10px - #resend_otp_container .resend-header font-size: 14px font-weight: var(--base-text-weight-bold) margin-bottom: 10px -#resend_otp_container .radios-wrapper - padding-top: 7px - float: left - border: none - overflow: hidden - clear: none - - -#resend_otp_container .radios-wrapper label - float: left - clear: none - display: block - padding: 2px 1em 0 0 - - -#resend_otp_container .radios-wrapper input[type=radio] - float: left - clear: none - margin: 2px 0 15px 2px // Add some paddings to action links .mobile-otp--two-factor-device-row td.buttons diff --git a/frontend/src/global_styles/layout/work_packages/_table_baseline_overrides.sass b/frontend/src/global_styles/layout/work_packages/_table_baseline_overrides.sass index e816fcfa557..aa85bfddeea 100644 --- a/frontend/src/global_styles/layout/work_packages/_table_baseline_overrides.sass +++ b/frontend/src/global_styles/layout/work_packages/_table_baseline_overrides.sass @@ -27,8 +27,8 @@ //++ .op-table-baseline--old-field - // Hide status bubbles in old values - &[class^=__hl_inline_status]:before + // Hide highlighting bubbles in old values + &[class^=__hl_inline_]:before display: none // Hide avatars in old values diff --git a/frontend/src/global_styles/openproject/_announcements.sass b/frontend/src/global_styles/openproject/_announcements.sass index 07fe3a4ca73..8a42ffdcbbc 100644 --- a/frontend/src/global_styles/openproject/_announcements.sass +++ b/frontend/src/global_styles/openproject/_announcements.sass @@ -4,3 +4,5 @@ margin-top: 1rem width: 511px + @media screen and (max-width: $breakpoint-sm) + width: 100% diff --git a/modules/grids/config/locales/js-en.yml b/modules/grids/config/locales/js-en.yml index b4c81602a84..44543cb5754 100644 --- a/modules/grids/config/locales/js-en.yml +++ b/modules/grids/config/locales/js-en.yml @@ -8,6 +8,7 @@ en: text: "Some widgets, like the work package graph widget, are only available in the Enterprise edition." link: 'Enterprise edition.' widgets: + missing_permission: "You don't have the necessary permissions to view this widget." custom_text: title: 'Custom text' documents: diff --git a/modules/storages/spec/features/create_file_links_spec.rb b/modules/storages/spec/features/create_file_links_spec.rb index 7074bb7df4b..0221ffaabf7 100644 --- a/modules/storages/spec/features/create_file_links_spec.rb +++ b/modules/storages/spec/features/create_file_links_spec.rb @@ -129,7 +129,7 @@ RSpec.describe 'Managing file links in work package', :js, :webmock do end it 'must enable the user to remove a file link' do - within_test_selector('op-tab-content--tab-section', text: 'MY STORAGE', wait: 25) do + within_test_selector('op-tab-content--tab-section', text: 'MY STORAGE') do within(:list_item, text: 'jingle.ogg') do page.find('span', text: 'jingle.ogg').hover page.click_on('Remove file link') diff --git a/modules/team_planner/spec/features/team_planner_context_menu_spec.rb b/modules/team_planner/spec/features/team_planner_context_menu_spec.rb index 3054cd7ab24..6813a415f08 100644 --- a/modules/team_planner/spec/features/team_planner_context_menu_spec.rb +++ b/modules/team_planner/spec/features/team_planner_context_menu_spec.rb @@ -43,9 +43,7 @@ RSpec.describe 'Work package table context menu', team_planner.visit! loading_indicator_saveguard - retry_block do - team_planner.add_assignee user - end + team_planner.add_assignee user team_planner.within_lane(user) do team_planner.expect_event work_package diff --git a/modules/team_planner/spec/features/team_planner_create_spec.rb b/modules/team_planner/spec/features/team_planner_create_spec.rb index 70de2980ba5..7bc0eee95ce 100644 --- a/modules/team_planner/spec/features/team_planner_create_spec.rb +++ b/modules/team_planner/spec/features/team_planner_create_spec.rb @@ -77,11 +77,7 @@ RSpec.describe 'Team planner create new work package', :js, with_ee: %i[team_pla team_planner.expect_assignee(user, present: false) - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add user.name - end + team_planner.add_assignee user.name end it_behaves_like 'can create a new work package' @@ -119,23 +115,9 @@ RSpec.describe 'Team planner create new work package', :js, with_ee: %i[team_pla team_planner.expect_assignee(other_user, present: false) team_planner.expect_assignee(third_user, present: false) - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add other_user.name - end - - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add third_user.name - end - - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add user.name - end + team_planner.add_assignee other_user.name + team_planner.add_assignee third_user.name + team_planner.add_assignee user.name end it_behaves_like 'can create a new work package' diff --git a/modules/team_planner/spec/features/team_planner_dates_spec.rb b/modules/team_planner/spec/features/team_planner_dates_spec.rb index e429e6b3b75..50d521d2b9f 100644 --- a/modules/team_planner/spec/features/team_planner_dates_spec.rb +++ b/modules/team_planner/spec/features/team_planner_dates_spec.rb @@ -41,11 +41,7 @@ RSpec.describe 'Team planner working days', :js, team_planner.visit! team_planner.expect_empty_state - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add user.name - end + team_planner.add_assignee user.name # Initially, in the "Work week" view, non working days are hidden expect(page).to have_css('.fc-day-mon') diff --git a/modules/team_planner/spec/features/team_planner_project_include_spec.rb b/modules/team_planner/spec/features/team_planner_project_include_spec.rb index 30391f5ef5a..3d5a42ad9a7 100644 --- a/modules/team_planner/spec/features/team_planner_project_include_spec.rb +++ b/modules/team_planner/spec/features/team_planner_project_include_spec.rb @@ -56,15 +56,11 @@ RSpec.describe 'Team planner project include', :js, with_ee: %i[team_planner_vie work_package_view.expect_assignee(other_user, present: false) retry_block do - work_package_view.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - work_package_view.select_user_to_add user.name + work_package_view.add_assignee user.name end retry_block do - work_package_view.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - work_package_view.select_user_to_add other_user.name + work_package_view.add_assignee other_user.name end work_package_view.expect_assignee user diff --git a/modules/team_planner/spec/features/team_planner_spec.rb b/modules/team_planner/spec/features/team_planner_spec.rb index 967e5784802..7326ac8e795 100644 --- a/modules/team_planner/spec/features/team_planner_spec.rb +++ b/modules/team_planner/spec/features/team_planner_spec.rb @@ -155,19 +155,11 @@ RSpec.describe 'Team planner', team_planner.expect_assignee(user, present: false) team_planner.expect_assignee(other_user, present: false) - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add user.name - end + team_planner.add_assignee user.name team_planner.expect_empty_state(present: false) - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add other_user.name - end + team_planner.add_assignee other_user.name team_planner.expect_assignee user team_planner.expect_assignee other_user @@ -249,21 +241,13 @@ RSpec.describe 'Team planner', team_planner.expect_assignee(user, present: false) team_planner.expect_assignee(other_user, present: false) - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add user.name - end + team_planner.add_assignee user.name team_planner.expect_empty_state(present: false) team_planner.expect_assignee(user) team_planner.expect_assignee(other_user, present: false) - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add other_user.name - end + team_planner.add_assignee other_user.name team_planner.expect_assignee(user) team_planner.expect_assignee(other_user) @@ -280,11 +264,7 @@ RSpec.describe 'Team planner', team_planner.expect_empty_state # Try one more time to make sure deleting the full filter didn't kill the functionality - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add user.name - end + team_planner.add_assignee user.name team_planner.expect_assignee(user) team_planner.expect_assignee(other_user, present: false) @@ -293,11 +273,7 @@ RSpec.describe 'Team planner', it 'filters possible assignees correctly' do team_planner.visit! - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.search_user_to_add user_outside_project.name - end + team_planner.search_assignee(user_outside_project.name) expect(page).to have_css('.ng-option-disabled', text: "No items found") @@ -307,14 +283,45 @@ RSpec.describe 'Team planner', team_planner.expect_assignee(user) - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.search_user_to_add user.name - end + team_planner.search_assignee user.name expect(page).to have_css('.ng-option-disabled', text: "No items found") end + + context 'when the page size is smaller than the number of assignees' do + before do + allow(Setting) + .to receive(:per_page_options_array) + .and_return([1]) + end + + it 'renders assignees and assignee dropdown correctly' do + team_planner.visit! + team_planner.wait_for_loaded + + # Render all the available users in the select dropdown regardless of the page size + team_planner.click_add_user + + team_planner.expect_user_selectable user + team_planner.expect_user_selectable other_user + + team_planner.add_assignee user + team_planner.add_assignee other_user + + team_planner.save_as('TP1') + page.refresh + team_planner.wait_for_loaded + + # Render all the available users in the team planner regardless of the page size + team_planner.expect_assignee user + team_planner.expect_assignee other_user + + # Do not render any available users in the select + team_planner.click_add_user + team_planner.expect_user_selectable user, present: false + team_planner.expect_user_selectable other_user, present: false + end + end end context 'with a readonly work package' do @@ -337,11 +344,7 @@ RSpec.describe 'Team planner', team_planner.expect_empty_state team_planner.expect_assignee(user, present: false) - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add user.name - end + team_planner.add_assignee user.name team_planner.expect_empty_state(present: false) team_planner.expect_assignee user diff --git a/modules/team_planner/spec/features/team_planner_subproject_constraints_spec.rb b/modules/team_planner/spec/features/team_planner_subproject_constraints_spec.rb index 5babad79c12..c3ff5fc2a77 100644 --- a/modules/team_planner/spec/features/team_planner_subproject_constraints_spec.rb +++ b/modules/team_planner/spec/features/team_planner_subproject_constraints_spec.rb @@ -58,9 +58,7 @@ RSpec.describe 'Team planner constraints for a subproject', team_planner.visit! team_planner.add_assignee user - retry_block do - team_planner.add_assignee other_user - end + team_planner.add_assignee other_user # Include the subproject project_include.toggle! diff --git a/modules/team_planner/spec/features/team_planner_user_interaction_spec.rb b/modules/team_planner/spec/features/team_planner_user_interaction_spec.rb index 457b26983ad..460e46f86d5 100644 --- a/modules/team_planner/spec/features/team_planner_user_interaction_spec.rb +++ b/modules/team_planner/spec/features/team_planner_user_interaction_spec.rb @@ -79,9 +79,7 @@ RSpec.describe 'Team planner drag&dop and resizing', team_planner.visit! team_planner.add_assignee user - retry_block do - team_planner.add_assignee other_user - end + team_planner.add_assignee other_user team_planner.within_lane(user) do team_planner.expect_event first_wp, present: false @@ -247,9 +245,7 @@ RSpec.describe 'Team planner drag&dop and resizing', team_planner.visit! team_planner.add_assignee user - retry_block do - team_planner.add_assignee other_user - end + team_planner.add_assignee other_user end it 'allows neither dragging nor resizing any wp' do diff --git a/modules/team_planner/spec/features/team_planner_view_modes_spec.rb b/modules/team_planner/spec/features/team_planner_view_modes_spec.rb index e7e786cc355..56ded17546f 100644 --- a/modules/team_planner/spec/features/team_planner_view_modes_spec.rb +++ b/modules/team_planner/spec/features/team_planner_view_modes_spec.rb @@ -36,11 +36,7 @@ RSpec.describe 'Team planner', :js, with_ee: %i[team_planner_view] do team_planner.visit! team_planner.expect_empty_state - retry_block do - team_planner.click_add_user - page.find("#{test_selector('tp-add-assignee')} input") - team_planner.select_user_to_add user.name - end + team_planner.add_assignee user.name team_planner.expect_view_mode 'Work week' expect(page).to have_css('.fc-timeline-slot-frame', count: 5) diff --git a/modules/team_planner/spec/support/pages/team_planner.rb b/modules/team_planner/spec/support/pages/team_planner.rb index de1656388b8..5b83fd3f574 100644 --- a/modules/team_planner/spec/support/pages/team_planner.rb +++ b/modules/team_planner/spec/support/pages/team_planner.rb @@ -225,9 +225,23 @@ module Pages end def add_assignee(name) - click_add_user - page.find("#{page.test_selector('tp-add-assignee')} input") - select_user_to_add name + retry_block do + return if page.has_selector?('.fc-resource', text: name, wait: 0) + + click_add_user + page.find("#{page.test_selector('tp-add-assignee')} input") + select_user_to_add(name) + end + end + + def search_assignee(name) + retry_block do + click_add_user + page.find("#{page.test_selector('tp-add-assignee')} input") + search_autocomplete page.find('[data-test-selector="tp-add-assignee"]'), + query: name, + results_selector: 'body' + end end def click_add_user @@ -243,10 +257,13 @@ module Pages results_selector: 'body' end - def search_user_to_add(name) - search_autocomplete page.find('[data-test-selector="tp-add-assignee"]'), - query: name, - results_selector: 'body' + def expect_user_selectable(user, present: true) + name = user.is_a?(User) ? user.name : user.to_s + + expect_ng_option page.find('[data-test-selector="tp-add-assignee"]'), + name, + results_selector: 'body', + present: end def change_wp_date_by_resizing(work_package, number_of_days:, is_start_date:) diff --git a/modules/two_factor_authentication/app/views/two_factor_authentication/authentication/enter_backup_code.html.erb b/modules/two_factor_authentication/app/views/two_factor_authentication/authentication/enter_backup_code.html.erb index da2ca788a88..4406a7d07c0 100644 --- a/modules/two_factor_authentication/app/views/two_factor_authentication/authentication/enter_backup_code.html.erb +++ b/modules/two_factor_authentication/app/views/two_factor_authentication/authentication/enter_backup_code.html.erb @@ -11,7 +11,8 @@ <%= styled_text_field_tag 'backup_code', nil, required: true, autocomplete: 'off', size: 20, maxlength: 20, tabindex: 1, autofocus: true %>
- + + <% end %> diff --git a/modules/two_factor_authentication/app/views/two_factor_authentication/authentication/request_otp.html.erb b/modules/two_factor_authentication/app/views/two_factor_authentication/authentication/request_otp.html.erb index 7f9a4f527cc..ddd616959e5 100644 --- a/modules/two_factor_authentication/app/views/two_factor_authentication/authentication/request_otp.html.erb +++ b/modules/two_factor_authentication/app/views/two_factor_authentication/authentication/request_otp.html.erb @@ -43,7 +43,7 @@ <% end %> - + <% if resend_supported || has_other_devices || has_backup_codes %>
+ + + <% end %> +
diff --git a/modules/two_factor_authentication/app/views/two_factor_authentication/two_factor_devices/confirm.html.erb b/modules/two_factor_authentication/app/views/two_factor_authentication/two_factor_devices/confirm.html.erb index 64107c8b5c0..44c45f1da3f 100644 --- a/modules/two_factor_authentication/app/views/two_factor_authentication/two_factor_devices/confirm.html.erb +++ b/modules/two_factor_authentication/app/views/two_factor_authentication/two_factor_devices/confirm.html.erb @@ -14,6 +14,6 @@
- +
<% end %> diff --git a/spec/support/components/autocompleter/ng_select_autocomplete_helpers.rb b/spec/support/components/autocompleter/ng_select_autocomplete_helpers.rb index ad401bf5526..63c90379730 100644 --- a/spec/support/components/autocompleter/ng_select_autocomplete_helpers.rb +++ b/spec/support/components/autocompleter/ng_select_autocomplete_helpers.rb @@ -45,9 +45,9 @@ module Components::Autocompleter end end - def expect_ng_option(element, option, results_selector: nil) + def expect_ng_option(element, option, results_selector: nil, present: true) within(ng_find_dropdown(element, results_selector:)) do - expect(page).to have_css('.ng-option', text: option) + expect(page).to have_conditional_selector(present, '.ng-option', text: option) end end