diff --git a/.github/workflows/docs-site.yml b/.github/workflows/docs-site.yml index 33dcbbb4ef..2289d771b7 100644 --- a/.github/workflows/docs-site.yml +++ b/.github/workflows/docs-site.yml @@ -37,11 +37,11 @@ jobs: - name: Set up PNPM uses: pnpm/action-setup@v2 with: - version: 7 - - name: Use Node.js 19 + version: 9 + - name: Use Node.js 20 uses: actions/setup-node@v3 with: - node-version: 19 + node-version: 20 cache: "pnpm" - name: Install dependencies run: pnpm install diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 75e6055a0c..eea3ad3305 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -8,7 +8,7 @@ on: jobs: test: - timeout-minutes: 10 + timeout-minutes: 15 runs-on: ubuntu-latest services: @@ -38,11 +38,11 @@ jobs: - name: Set up PNPM uses: pnpm/action-setup@v2 with: - version: 7 - - name: Use Node.js 19 + version: 9 + - name: Use Node.js 20 uses: actions/setup-node@v3 with: - node-version: 19 + node-version: 20 cache: "pnpm" - name: Install dependencies run: pnpm install diff --git a/Dockerfile b/Dockerfile index f6eaf04ce6..713d0125ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,8 +30,8 @@ RUN apt-get update -qq && \ python-is-python3 # Install JavaScript dependencies -ARG NODE_VERSION=19.7.0 -ARG PNPM_VERSION=7.29.1 +ARG NODE_VERSION=20.14.0 +ARG PNPM_VERSION=9.2.0 ENV PATH=/usr/local/node/bin:$PATH RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \ /tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \ diff --git a/Gemfile.lock b/Gemfile.lock index bd9281e15c..a204e8416d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -342,6 +342,8 @@ GEM net-smtp (0.5.0) net-protocol nio4r (2.7.3) + nokogiri (1.16.5-aarch64-linux) + racc (~> 1.4) nokogiri (1.16.5-arm64-darwin) racc (~> 1.4) nokogiri (1.16.5-x86_64-darwin) @@ -545,6 +547,8 @@ GEM strscan (3.1.0) syntax_tree (6.2.0) prettier_print (>= 1.2.0) + tailwindcss-rails (2.6.0-aarch64-linux) + railties (>= 7.0.0) tailwindcss-rails (2.6.0-arm64-darwin) railties (>= 7.0.0) tailwindcss-rails (2.6.0-x86_64-darwin) @@ -593,6 +597,7 @@ GEM zip_kit (6.3.0) PLATFORMS + aarch64-linux arm64-darwin-22 arm64-darwin-23 x86_64-darwin-21 diff --git a/app/components/samples/table_component.en.yml b/app/components/samples/table_component.en.yml new file mode 100644 index 0000000000..9ef169d1cd --- /dev/null +++ b/app/components/samples/table_component.en.yml @@ -0,0 +1,12 @@ +en: + puid: ID + name: Sample + project: Project + created_at: Created + updated_at: Last Updated + attachments_updated_at: Files Last Updated + action: Action + counts: + samples: Samples + selected: Selected + select_all: Select All diff --git a/app/components/samples/table_component.fr.yml b/app/components/samples/table_component.fr.yml new file mode 100644 index 0000000000..1fa5de6999 --- /dev/null +++ b/app/components/samples/table_component.fr.yml @@ -0,0 +1,12 @@ +fr: + puid: ID + name: Sample + project: Project + created_at: Created + updated_at: Last Updated + attachments_updated_at: Files Last Updated + action: Action + counts: + samples: Samples + selected: Selected + select_all: Select All diff --git a/app/components/samples/table_component.html.erb b/app/components/samples/table_component.html.erb new file mode 100644 index 0000000000..466c638727 --- /dev/null +++ b/app/components/samples/table_component.html.erb @@ -0,0 +1,260 @@ +<%= render Viral::BaseComponent.new(**wrapper_arguments) do %> + <%= render Viral::BaseComponent.new(**system_arguments) do %> + + + + <% @columns.each_with_index do |column, index| %> + <%= render_cell( + tag: 'th', + scope: 'col', + classes: class_names('px-6 py-3', 'sticky left-0 z-10 flex': index.zero?) + ) do %> + <% if index.zero? and @abilities[:select_samples] %> + <%= search_form_for( + @q, + url: select_samples_url(**request.query_parameters), + html: { "data-controller": "filters", id: "select-all-form" }, + class: "filters align-middle" + ) do |f| %> + + + <% @search_params.each do |key, value| %> + <% if value.is_a?(Array) %> + <% value.each do |val| %> + + <% end %> + <% else %> + + <% end %> + <% end %> + + + + <% end %> + <% end %> + + <% if column == :attachments_updated_at %> + <%= render Ransack::SortComponent.new( + ransack_obj: @q, + label: t(".#{column}"), + url: helpers.sorting_url(@q, :attachments_updated_at_nulls_last), + field: :attachments_updated_at_nulls_last, + ) %> + <% else %> + <%= render Ransack::SortComponent.new( + ransack_obj: @q, + label: t(".#{column}"), + url: helpers.sorting_url(@q, column), + field: column, + ) %> + <% end %> + <% end %> + <% end %> + <% @metadata_fields.each do |field| %> + <%= render_cell( + tag: 'th', + scope: 'col', + class: class_names('px-6 py-3') + ) do %> + <%= render Ransack::SortComponent.new( + ransack_obj: @q, + label: field, + url: + helpers.sorting_url(@q, URI.encode_www_form_component("metadata_#{field}")), + field: "metadata_#{field}", + ) %> + <% end %> + <% end %> + <% if @renders_row_actions %> + <%= render_cell( + tag: 'th', + scope: 'col', + classes: class_names('px-6 py-3 bg-slate-50 dark:bg-slate-700 sticky right-0') + ) do %> + <%= t(".action") %> + <% end %> + <% end %> + + + + <% @samples.each do |sample| %> + <%= render Viral::BaseComponent.new(**row_arguments(sample)) do %> + <% @columns.each_with_index do |column, index| %> + <%= render_cell( + tag: index.zero? ? 'th' :'td', + scope: index.zero? ? 'row' : nil, + classes: class_names('px-6 py-3', 'sticky left-0 bg-slate-50 dark:bg-slate-900': index.zero?) + ) do %> + <% if index.zero? && @abilities[:select_samples] %> + <%= check_box_tag "sample_ids[]", + sample.id, + nil, + id: dom_id(sample), + "aria-label": sample.name, + data: { + action: "input->selection#toggle", + selection_target: "rowSelection", + }, + class: + "w-4 h-4 mr-2.5 text-primary-600 bg-slate-100 border-slate-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-slate-800 focus:ring-2 dark:bg-slate-700 dark:border-slate-600" %> + <% end %> + <% if column == :puid || column == :name %> + <%= link_to( + project_sample_path(sample.project, sample), + data: { turbo: false }, + class: "text-slate-900 dark:text-slate-100 font-semibold hover:underline" + ) do %> + + <%= highlight( + sample[column], + defined?(params[:q][:name_or_puid_cont]) && params[:q][:name_or_puid_cont], + highlighter: '\1', + ) %> + + <% end %> + <% elsif column == :project %> + <%= viral_tooltip(title: project_path(sample.project)) do %> + <%= link_to sample.project.abbreviated_path, + project_path(sample.project), + data: { + turbo: false, + }, + class: "text-slate-900 dark:text-slate-100 font-semibold hover:underline" %> + <% end %> + <% elsif column == :created_at %> + <%= l(sample[column].localtime, format: :full_date) %> + <% elsif column == :updated_at || column == :attachments_updated_at %> + <% if sample[column].present? %> + <%= viral_time_ago(original_time: sample[column]) %> + <% end %> + <% else %> + <%= sample[column.to_sym] %> + <% end %> + <% end %> + <% end %> + <% @metadata_fields.each do |field| %> + <%= render_cell( + tag: 'td', + scope: 'col', + class: class_names('px-6 py-3') + ) do %> + <%= sample.metadata[field] %> + <% end %> + <% end %> + <% if @renders_row_actions %> + <%= render_cell( + tag: 'td', + classes: class_names('px-6 py-3 sticky right-0 bg-white dark:bg-slate-800 z-10 space-x-2') + ) do %> + <% if @row_actions[:edit] %> + <%= link_to( + t(:"projects.samples.index.edit_button"), + edit_project_sample_path(sample.project, sample), + data: { + turbo: false, + }, + class: + "font-medium text-blue-600 underline dark:text-blue-500 hover:no-underline cursor-pointer", + ) %> + <% end %> + <% if @row_actions[:destroy] %> + <%= link_to( + t(:"projects.samples.index.remove_button"), + project_sample_path(sample.project, sample), + data: { + turbo_method: :delete, + turbo_confirm: t(:"projects.samples.index.remove_button_confirmation"), + }, + class: + "font-medium text-blue-600 underline dark:text-blue-500 hover:no-underline cursor-pointer", + ) %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + + <% if @abilities[:select_samples] && @has_samples %> + + + + + + + <% end %> +
+ + <%= t(".counts.samples") %>: + <%= @pagy.count %> + + + <%= t(".counts.selected") %>: + 0 + +
+ <% end %> + <% if @has_samples %> + <%= render PaginationComponent.new( + info: helpers.pagy_info(@pagy), + prev_url: + ( + if @pagy.prev + helpers.pagy_url_for(@pagy, @pagy.prev).gsub( + "samples.turbo_stream", + "samples", + ) + else + nil + end + ), + next_url: + ( + if @pagy.next + helpers.pagy_url_for(@pagy, @pagy.next).gsub( + "samples.turbo_stream", + "samples", + ) + else + nil + end + ), + ) %> + <% end %> +
+ <%= viral_empty( + title: @empty[:title], + description: @empty[:description], + icon_name: :beaker, + ) %> +
+<% end %> diff --git a/app/components/samples/table_component.rb b/app/components/samples/table_component.rb new file mode 100644 index 0000000000..fb3d2edbeb --- /dev/null +++ b/app/components/samples/table_component.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +module Samples + # Component for rendering a table of Samples + class TableComponent < Component + include Ransack::Helpers::FormHelper + + # rubocop:disable Naming/MethodParameterName,Metrics/ParameterLists + def initialize( + samples, + namespace, + pagy, + q, + has_samples: true, + abilities: {}, + metadata_fields: [], + search_params: {}, + row_actions: {}, + empty: {}, + **system_arguments + ) + @samples = samples + @namespace = namespace + @pagy = pagy + @q = q + @has_samples = has_samples + @abilities = abilities + @metadata_fields = metadata_fields + @search_params = search_params + @row_actions = row_actions + @empty = empty + @renders_row_actions = @row_actions.select { |_key, value| value }.count.positive? + @system_arguments = system_arguments + + @columns = columns + end + # rubocop:enable Naming/MethodParameterName,Metrics/ParameterLists + + def system_arguments + { tag: 'div' }.deep_merge(@system_arguments).tap do |args| + args[:id] = 'samples-table' + args[:classes] = class_names(args[:classes], 'relative', 'overflow-x-auto') + if @abilities[:select_samples] + args[:data] ||= {} + args[:data][:controller] = 'selection' + args[:data][:'selection-total-value'] = @pagy.count + args[:data][:'selection-action-link-outlet'] = '.action-link' + end + end + end + + def wrapper_arguments + { + tag: 'div', + classes: class_names('table-container'), + data: { turbo: :temporary } + } + end + + def row_arguments(sample) + { tag: 'tr' }.tap do |args| + args[:classes] = class_names('bg-white', 'border-b', 'dark:bg-slate-800', 'dark:border-slate-700') + args[:id] = sample.id + end + end + + def render_cell(**arguments, &) + render(Viral::BaseComponent.new(**arguments), &) + end + + def select_samples_url(**) + if @namespace.type == 'Group' + select_group_samples_url(@namespace, **) + else + select_namespace_project_samples_url(@namespace.parent, @namespace.project, **) + end + end + + private + + def columns + columns = %i[puid name] + columns << :project if @namespace.type == 'Group' + columns += %i[created_at updated_at attachments_updated_at] + columns + end + end +end diff --git a/app/controllers/groups/samples_controller.rb b/app/controllers/groups/samples_controller.rb index b10f3684a7..4fbdec8fde 100644 --- a/app/controllers/groups/samples_controller.rb +++ b/app/controllers/groups/samples_controller.rb @@ -7,19 +7,18 @@ class SamplesController < Groups::ApplicationController before_action :group, :current_page before_action :set_search_params, only: %i[index] + before_action :set_metadata_fields, only: %i[index] - def index # rubocop:disable Metrics/AbcSize + def index authorize! @group, to: :sample_listing? + @q = authorized_samples.ransack(params[:q]) set_default_sort + @pagy, @samples = pagy_with_metadata_sort(@q.result) + @has_samples = authorized_samples.count.positive? respond_to do |format| - format.html do - @has_samples = @q.result.count.positive? - end - format.turbo_stream do - @pagy, @samples = pagy_with_metadata_sort(@q.result) - fields_for_namespace(namespace: @group, show_fields: params[:q] && params[:q][:metadata].to_i == 1) - end + format.html + format.turbo_stream end end @@ -76,5 +75,9 @@ def set_default_sort def set_search_params @search_params = params[:q].nil? ? {} : params[:q].to_unsafe_h end + + def set_metadata_fields + fields_for_namespace(namespace: @group, show_fields: params[:q] && params[:q][:metadata].to_i == 1) + end end end diff --git a/app/controllers/projects/samples_controller.rb b/app/controllers/projects/samples_controller.rb index 1a0528071e..46731c25e0 100644 --- a/app/controllers/projects/samples_controller.rb +++ b/app/controllers/projects/samples_controller.rb @@ -8,23 +8,18 @@ class SamplesController < Projects::ApplicationController # rubocop:disable Metr before_action :sample, only: %i[show edit update destroy view_history_version] before_action :current_page before_action :set_search_params, only: %i[index destroy] + before_action :set_metadata_fields, only: :index - def index # rubocop:disable Metrics/AbcSize + def index authorize! @project, to: :sample_listing? @q = load_samples.ransack(params[:q]) set_default_sort + @pagy, @samples = pagy_with_metadata_sort(@q.result) + @has_samples = load_samples.count.positive? respond_to do |format| - format.html do - @has_samples = @q.result.count.positive? - end - format.turbo_stream do - @pagy, @samples = pagy_with_metadata_sort(@q.result) - fields_for_namespace( - namespace: @project.namespace, - show_fields: params[:q] && params[:q][:metadata].to_i == 1 - ) - end + format.html + format.turbo_stream end end @@ -181,5 +176,12 @@ def set_default_sort def set_search_params @search_params = params[:q].nil? ? {} : params[:q].to_unsafe_h end + + def set_metadata_fields + fields_for_namespace( + namespace: @project.namespace, + show_fields: params[:q] && params[:q][:metadata].to_i == 1 + ) + end end end diff --git a/app/javascript/controllers/action_link_controller.js b/app/javascript/controllers/action_link_controller.js index 0a8685fee0..32b6e031c7 100644 --- a/app/javascript/controllers/action_link_controller.js +++ b/app/javascript/controllers/action_link_controller.js @@ -12,6 +12,10 @@ export default class extends Controller { #primary_colours = ["bg-primary-200", "text-slate-400", "border-primary-200"]; + connect() { + this.setDisabled(); + } + setDisabled(count = 0) { if (this.requiredValue > count) { this.element.setAttribute("aria-disabled", "true"); diff --git a/app/javascript/controllers/selection_controller.js b/app/javascript/controllers/selection_controller.js index bffed1047a..0914518a1a 100644 --- a/app/javascript/controllers/selection_controller.js +++ b/app/javascript/controllers/selection_controller.js @@ -4,9 +4,9 @@ export default class extends Controller { // # indicates private attribute or method // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_properties #storageKey = null; - #total = 0; + #numSelected = 0; - static targets = ["rowSelection", "selectAll", "total", "selected"]; + static targets = ["rowSelection", "selectAll", "selected"]; static outlets = ["action-link"]; static values = { @@ -27,12 +27,12 @@ export default class extends Controller { if (storageValue) { this.#updateUI(storageValue); - this.#total = storageValue.length; + this.#numSelected = storageValue.length; } else { this.save([]); } - this.#updatedCounts(storageValue.length); + this.#updateCounts(storageValue.length); } actionLinkOutletConnected(outlet) { @@ -55,7 +55,7 @@ export default class extends Controller { save(storageValue) { sessionStorage.setItem(this.#storageKey, JSON.stringify([...storageValue])); - this.#total = storageValue.length; + this.#numSelected = storageValue.length; } update(ids) { @@ -63,8 +63,8 @@ export default class extends Controller { this.#updateUI(ids); } - getTotal() { - return this.#total; + getNumSelected() { + return this.#numSelected; } getStoredSamples() { @@ -86,7 +86,7 @@ export default class extends Controller { this.save(newStorageValue); this.#updateActionLinks(newStorageValue.length); this.#setSelectAllCheckboxValue(newStorageValue.length); - this.#updatedCounts(newStorageValue.length); + this.#updateCounts(newStorageValue.length); } #updateUI(ids) { @@ -95,7 +95,7 @@ export default class extends Controller { }); this.#updateActionLinks(ids.length); this.#setSelectAllCheckboxValue(ids.length); - this.#updatedCounts(ids.length); + this.#updateCounts(ids.length); } #updateActionLinks(count) { @@ -104,15 +104,14 @@ export default class extends Controller { }); } - #setSelectAllCheckboxValue(total) { - if (this.hasSelectAllTarget) { - this.selectAllTarget.checked = this.totalValue === total; + #setSelectAllCheckboxValue(numSelected) { + if (this.hasSelectAllTarget && this.totalValue > 0) { + this.selectAllTarget.checked = this.totalValue === numSelected; } } - #updatedCounts(selected) { - if (this.hasTotalTarget && this.hasSelectedTarget) { - this.totalTarget.innerText = this.totalValue; + #updateCounts(selected) { + if (this.hasSelectedTarget) { this.selectedTarget.innerText = selected; } } diff --git a/app/javascript/controllers/workflow_selection_controller.js b/app/javascript/controllers/workflow_selection_controller.js index fb81ecac16..0d8a9ae390 100644 --- a/app/javascript/controllers/workflow_selection_controller.js +++ b/app/javascript/controllers/workflow_selection_controller.js @@ -35,9 +35,9 @@ export default class extends Controller { for (const workflow of this.workflowTargets) { if ( params.workflowname !== - workflow.dataset.workflowSelectionWorkflownameParam || + workflow.dataset.workflowSelectionWorkflownameParam || params.workflowversion !== - workflow.dataset.workflowSelectionWorkflowversionParam + workflow.dataset.workflowSelectionWorkflowversionParam ) { workflow.classList.add("hidden"); } else { @@ -52,7 +52,7 @@ export default class extends Controller { const wsLoading = workflow.querySelector(".ws-loading-text"); wsLoading.textContent = wsLoading.textContent.replace( "COUNT_PLACEHOLDER", - this.selectionOutlet.getTotal(), + this.selectionOutlet.getNumSelected(), ); } } diff --git a/app/views/dashboard/projects/index.html.erb b/app/views/dashboard/projects/index.html.erb index 7216e59355..142d7f0a3a 100644 --- a/app/views/dashboard/projects/index.html.erb +++ b/app/views/dashboard/projects/index.html.erb @@ -83,7 +83,7 @@
<% if @has_projects %> - <%= turbo_frame_tag "projects_list", src: dashboard_projects_url(format: :turbo_stream, **request.query_parameters), loading: :lazy do %> + <%= turbo_frame_tag "projects_list", src: dashboard_projects_url(format: :turbo_stream, **request.query_parameters) do %> <%= render partial: "shared/loading/table" %> <% end %> <%= turbo_frame_tag "projects_pagination" %> diff --git a/app/views/data_exports/show.html.erb b/app/views/data_exports/show.html.erb index e1a6418a0a..92a51841bf 100644 --- a/app/views/data_exports/show.html.erb +++ b/app/views/data_exports/show.html.erb @@ -72,8 +72,7 @@
<%= turbo_frame_tag "data-export-listing", - src: data_export_path(format: :turbo_stream, tab: @tab || 'summary'), - loading: :lazy do %> + src: data_export_path(format: :turbo_stream, tab: @tab || 'summary') do %> <% if @tab == "preview" %> <%= render partial: "shared/loading/list" %> <% else %> diff --git a/app/views/groups/bots/index.html.erb b/app/views/groups/bots/index.html.erb index b27d3286cf..994f859732 100644 --- a/app/views/groups/bots/index.html.erb +++ b/app/views/groups/bots/index.html.erb @@ -43,8 +43,7 @@ <%= turbo_frame_tag "bots", src: group_bots_path( format: :turbo_stream - ), - loading: :lazy do %> + ) do %> <%= render partial: "shared/loading/table" %> <% end %> <%= turbo_frame_tag "bots_pagination" %> diff --git a/app/views/groups/history/index.html.erb b/app/views/groups/history/index.html.erb index 5e76036f26..679a18440e 100644 --- a/app/views/groups/history/index.html.erb +++ b/app/views/groups/history/index.html.erb @@ -6,6 +6,5 @@ <%= turbo_frame_tag "history_modal" %> <%= turbo_frame_tag "group_history", - src: group_history_url(format: :turbo_stream, **request.query_parameters), - loading: :lazy %> + src: group_history_url(format: :turbo_stream, **request.query_parameters) %>
diff --git a/app/views/groups/members/index.html.erb b/app/views/groups/members/index.html.erb index 6b985d8d4a..3e522adfe2 100644 --- a/app/views/groups/members/index.html.erb +++ b/app/views/groups/members/index.html.erb @@ -6,7 +6,7 @@ new_group_group_link_path(@namespace, tab: @tab), data: { "turbo-frame" => "new_member_modal", - :turbo_stream => true + :turbo_stream => true, }, class: "button button--state-default button--size-default" %> <% end %> @@ -15,7 +15,7 @@ new_group_member_path(@namespace, tab: @tab), data: { turbo_frame: "new_member_modal", - turbo_stream: true + turbo_stream: true, }, class: "button button--state-primary button--size-default", "aria-label": t(:".actions.button_add_aria_label") %> @@ -48,8 +48,7 @@ format: :turbo_stream ) end - ), - loading: :lazy do %> + ) do %> <%= render partial: "shared/loading/table" %> <% end %> <%= turbo_frame_tag "members_pagination", "data-turbo-temporary": true %> diff --git a/app/views/groups/samples/_pagination.html.erb b/app/views/groups/samples/_pagination.html.erb deleted file mode 100644 index 2ff9712019..0000000000 --- a/app/views/groups/samples/_pagination.html.erb +++ /dev/null @@ -1,19 +0,0 @@ -<%= render PaginationComponent.new( - info: pagy_info(@pagy), - prev_url: - ( - if @pagy.prev - pagy_url_for(@pagy, @pagy.prev).gsub("samples.turbo_stream", "samples") - else - nil - end - ), - next_url: - ( - if @pagy.next - pagy_url_for(@pagy, @pagy.next).gsub("samples.turbo_stream", "samples") - else - nil - end - ) -) %> diff --git a/app/views/groups/samples/_table.html.erb b/app/views/groups/samples/_table.html.erb index a9fb2f8d73..ce10e435de 100644 --- a/app/views/groups/samples/_table.html.erb +++ b/app/views/groups/samples/_table.html.erb @@ -1,274 +1,19 @@ -
- - - - - - - - - - <% @fields.each do |column| %> - - <% end %> - - - - <% samples.each do |sample| %> - - - - - - - - <% @fields.each do |column| %> - - <% end %> - - <% end %> - - - - - - - -
- <%= search_form_for @q, url: select_group_samples_url(**request.query_parameters), html: { "data-controller": "filters", id: "select-all-form" }, class: "filters flex align-middle" do |f| %> - - - <% @search_params.each do |key, value| %> - <% if value.is_a?(Array) %> - <% value.each do |val| %> - - <% end %> - <% else %> - - <% end %> - <% end %> - - <% if allowed_to?(:submit_workflow?, @group) %> - - - <% end %> - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".puid"), - url: sorting_url(@q, :puid), - field: :puid - ) %> - <% end %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".sample"), - url: sorting_url(@q, :name), - field: :name - ) %> - - <%= t(".project") %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".created_at"), - url: sorting_url(@q, :created_at), - field: :created_at - ) %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".updated_at"), - url: sorting_url(@q, :updated_at), - field: :updated_at - ) %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".files_updated_at"), - url: sorting_url(@q, :attachments_updated_at_nulls_last), - field: :attachments_updated_at_nulls_last - ) %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: column, - url: sorting_url(@q, URI.encode_www_form_component("metadata_#{column}")), - field: "metadata_#{column}" - ) %> -
- <% if allowed_to?(:submit_workflow?, @group) %> - <%= check_box_tag "sample_ids[]", - sample.id, - nil, - id: dom_id(sample), - "aria-label": sample.name, - data: { - action: "input->selection#toggle", - selection_target: "rowSelection" - }, - class: - "w-4 h-4 mr-2.5 text-primary-600 bg-slate-100 border-slate-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-slate-800 focus:ring-2 dark:bg-slate-700 dark:border-slate-600" %> - <% end %> - <%= link_to namespace_project_sample_path( - id: sample.id, - namespace_id: sample.project.namespace.parent.full_path, - project_id: sample.project.path - ), - data: { - turbo: false - }, - class: "text-slate-900 dark:text-slate-100 font-semibold hover:underline" do %> - - <%= highlight( - sample.puid, - defined?(params[:q][:name_or_puid_cont]) && params[:q][:name_or_puid_cont], - highlighter: '\1' - ) %> - - <% end %> - - <%= link_to namespace_project_sample_path( - id: sample.id, - namespace_id: sample.project.namespace.parent.full_path, - project_id: sample.project.path - ), - data: { - turbo: false - }, - class: "text-slate-900 dark:text-slate-100 font-semibold hover:underline" do %> - - <%= highlight( - sample.name, - defined?(params[:q][:name_or_puid_cont]) && params[:q][:name_or_puid_cont], - highlighter: '\1' - ) %> - - <% end %> - - <%= viral_tooltip(title: project_path(sample.project)) do %> - <%= link_to sample.project.abbreviated_path, - project_path(sample.project), - data: { - turbo: false - }, - class: "text-slate-900 dark:text-slate-100 font-semibold hover:underline" %> - <% end %> - - <%= l(sample.created_at.localtime, format: :full_date) %> - - <%= viral_time_ago(original_time: sample.updated_at) %> - - <% if sample.attachments_updated_at.present? %> - <%= viral_time_ago(original_time: sample.attachments_updated_at) %> - <% end %> - - <%= sample.metadata[column] %> -
- - - <%= t(".counts.samples") %>: - 0 - - - <%= t(".counts.selected") %>: - 0 - -
-
+<%# locals: (group:, pagy:, samples:) -%> +<%= render Samples::TableComponent.new( + samples, + group, + pagy, + @q, + has_samples: @has_samples, + abilities: { + select_samples: + allowed_to?(:submit_workflow?, group) || + allowed_to?(:export_sample_data?, group), + }, + metadata_fields: @fields, + search_params: @search_params, + empty: { + title: t(:"groups.samples.table.no_samples"), + description: t(:"groups.samples.table.no_associated_samples"), + }, +) %> diff --git a/app/views/groups/samples/index.html.erb b/app/views/groups/samples/index.html.erb index 7415244b57..dbf2ecae66 100644 --- a/app/views/groups/samples/index.html.erb +++ b/app/views/groups/samples/index.html.erb @@ -28,7 +28,6 @@ <% end %> <% if @has_samples %> - <%= search_form_for @q, url: group_samples_url(**request.query_parameters), html: { "data-controller": "filters","data-filters-selection-outlet": "#samples-table", class: "mb-2" } do |f| %>
@@ -55,16 +54,13 @@
<% end %> +<% end %> -
-
-
- <%= turbo_frame_tag "group_samples_list", - src: group_samples_path(@group, format: :turbo_stream, page: params[:page]) do %> - <%= render partial: "shared/loading/table" %> - <% end %> - <%= turbo_frame_tag "group_samples_pagination" %> -
-
-
+<%= turbo_frame_tag "group_samples_table" do %> + <%= render partial: "table", + locals: { + samples: @samples, + group: @group, + pagy: @pagy, + } %> <% end %> diff --git a/app/views/groups/samples/index.turbo_stream.erb b/app/views/groups/samples/index.turbo_stream.erb index 2bb8f5c6d2..f1e9089ef7 100644 --- a/app/views/groups/samples/index.turbo_stream.erb +++ b/app/views/groups/samples/index.turbo_stream.erb @@ -1,16 +1,11 @@ -<%= turbo_stream.update "group_samples_list", +<%= turbo_stream.update "group_samples_table", partial: "table", locals: { group: @group, + pagy: @pagy, samples: @samples, } %> -<%= turbo_stream.update "group_samples_pagination", - partial: "pagination", - locals: { - pagy: @pagy - } %> - <%= turbo_stream.update "group_samples_hidden_values" do %> <%= render Ransack::HiddenSortFieldComponent.new(@q) %> <% end %> diff --git a/app/views/groups/show.html.erb b/app/views/groups/show.html.erb index 0d8a7e7390..5f68f2d1f0 100644 --- a/app/views/groups/show.html.erb +++ b/app/views/groups/show.html.erb @@ -33,7 +33,7 @@ else group_subgroups_path(@group, format: :turbo_stream) end - ), loading: :lazy do %> + ) do %> + <%= capybara_lockstep if defined?(Capybara::Lockstep) %> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> <%= render "shared/dark_mode_check" %> - <%= capybara_lockstep if defined?(Capybara::Lockstep) %> diff --git a/app/views/projects/automated_workflow_executions/index.html.erb b/app/views/projects/automated_workflow_executions/index.html.erb index 5f3035d522..bba6efddc8 100644 --- a/app/views/projects/automated_workflow_executions/index.html.erb +++ b/app/views/projects/automated_workflow_executions/index.html.erb @@ -20,8 +20,7 @@ <%= turbo_frame_tag "automated_workflow_executions_table", src: namespace_project_automated_workflow_executions_path( format: :turbo_stream - ), - loading: :lazy do %> + ) do %> <%= render partial: "shared/loading/table" %> <% end %> diff --git a/app/views/projects/bots/index.html.erb b/app/views/projects/bots/index.html.erb index 1e2d34a616..e63c423436 100644 --- a/app/views/projects/bots/index.html.erb +++ b/app/views/projects/bots/index.html.erb @@ -6,32 +6,32 @@ new_namespace_project_bot_path(@namespace.parent, @namespace.project), data: { turbo_frame: "bot_modal", - turbo_stream: true + turbo_stream: true, }, class: " - inline-flex - items-center - justify-center - text-sm - border - rounded-md - cursor-pointer - sm:w-auto focus:z-10 - focus:outline-none - text-white - bg-primary-700 - hover:bg-primary-800 - focus:ring-0 - rounded-md - px-5 - py-3 - dark:text-white - dark:bg-primary-600 - dark:hover:bg-primary-700 - dark:border-primary-900 - dark:hover:bg-primary-700 - " %> + inline-flex + items-center + justify-center + text-sm + border + rounded-md + cursor-pointer + sm:w-auto focus:z-10 + focus:outline-none + text-white + bg-primary-700 + hover:bg-primary-800 + focus:ring-0 + rounded-md + px-5 + py-3 + dark:text-white + dark:bg-primary-600 + dark:hover:bg-primary-700 + dark:border-primary-900 + dark:hover:bg-primary-700 + " %> <% end %> <% end %> <% end %> @@ -44,8 +44,7 @@ <%= turbo_frame_tag "bots", src: namespace_project_bots_path( format: :turbo_stream - ), - loading: :lazy do %> + ) do %> <%= render partial: "shared/loading/table" %> <% end %> <%= turbo_frame_tag "bots_pagination" %> diff --git a/app/views/projects/history/index.html.erb b/app/views/projects/history/index.html.erb index 0d1455aa8b..343234a0fe 100644 --- a/app/views/projects/history/index.html.erb +++ b/app/views/projects/history/index.html.erb @@ -10,8 +10,7 @@ namespace_project_history_url( format: :turbo_stream, **request.query_parameters - ), - loading: :lazy do %> + ) do %> <%= render partial: "shared/loading/history" %> <% end %> diff --git a/app/views/projects/members/index.html.erb b/app/views/projects/members/index.html.erb index 735926d9fa..b4d50fd686 100644 --- a/app/views/projects/members/index.html.erb +++ b/app/views/projects/members/index.html.erb @@ -6,11 +6,11 @@ new_namespace_project_group_link_path( @namespace.parent, @namespace.project, - tab: @tab + tab: @tab, ), data: { "turbo-frame" => "new_member_modal", - :turbo_stream => true + :turbo_stream => true, }, class: "button button--state-default button--size-default" %> <% end %> @@ -19,11 +19,11 @@ new_namespace_project_member_path( @namespace.parent, @namespace.project, - tab: @tab + tab: @tab, ), data: { turbo_frame: "new_member_modal", - turbo_stream: true + turbo_stream: true, }, class: "button button--state-primary button--size-default", "aria-label": t(:".actions.button_add_aria_label") %> @@ -58,16 +58,11 @@ format: :turbo_stream ) end - ), - loading: :lazy do %> + ) do %>
<% 10.times do %> diff --git a/app/views/projects/samples/_pagination.html.erb b/app/views/projects/samples/_pagination.html.erb deleted file mode 100644 index 9f86866284..0000000000 --- a/app/views/projects/samples/_pagination.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<%= render PaginationComponent.new( - info: pagy_info(@pagy), - prev_url: (@pagy.prev ? pagy_url_for(@pagy, @pagy.prev).gsub("samples.turbo_stream", "samples") : nil), - next_url: (@pagy.next ? pagy_url_for(@pagy, @pagy.next).gsub("samples.turbo_stream", "samples") : nil) -) %> diff --git a/app/views/projects/samples/_table.html.erb b/app/views/projects/samples/_table.html.erb index 2dd03aaa20..7979a075f0 100644 --- a/app/views/projects/samples/_table.html.erb +++ b/app/views/projects/samples/_table.html.erb @@ -1,280 +1,25 @@ -
-
- - - - - - - - <% @fields.each do |column| %> - - <% end %> - <% if allowed_to?(:update_sample?, project) %> - - <% end %> - - - - <% samples.each do |sample| %> - - - - - - - <% @fields.each do |column| %> - - <% end %> - <% if allowed_to?(:update_sample?, project) %> - - <% end %> - - <% end %> - - <% if allowed_to?(:submit_workflow?, project) || allowed_to?(:clone_sample?, project) || allowed_to?(:transfer_sample?, project) %> - - - - - - - <% end %> -
- <%= search_form_for @q, url: select_namespace_project_samples_url(**request.query_parameters), html: { "data-controller": "filters", id: "select-all-form" }, class: "filters flex align-middle" do |f| %> - - - <% @search_params.each do |key, value| %> - <% if value.is_a?(Array) %> - <% value.each do |val| %> - - <% end %> - <% else %> - - <% end %> - <% end %> - - <% if allowed_to?(:submit_workflow?, project) || allowed_to?(:clone_sample?, project) || allowed_to?(:transfer_sample?, project) %> - - - <% end %> - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".puid"), - url: sorting_url(@q, :puid), - field: :puid - ) %> - <% end %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".sample"), - url: sorting_url(@q, :name), - field: :name - ) %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".created_at"), - url: sorting_url(@q, :created_at), - field: :created_at - ) %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".updated_at"), - url: sorting_url(@q, :updated_at), - field: :updated_at - ) %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: t(".files_updated_at"), - url: sorting_url(@q, :attachments_updated_at_nulls_last), - field: :attachments_updated_at_nulls_last - ) %> - - <%= render Ransack::SortComponent.new( - ransack_obj: @q, - label: column, - url: sorting_url(@q, URI.encode_www_form_component("metadata_#{column}")), - field: "metadata_#{column}" - ) %> - <%= t(".action") %>
- <% if allowed_to?(:submit_workflow?, project) || allowed_to?(:clone_sample?, project) || allowed_to?(:transfer_sample?, project) %> - <%= check_box_tag "sample_ids[]", - sample.id, - nil, - id: dom_id(sample), - "aria-label": sample.name, - data: { - action: "input->selection#toggle", - selection_target: "rowSelection" - }, - class: - "w-4 h-4 mr-2.5 text-primary-600 bg-slate-100 border-slate-300 rounded focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-slate-800 focus:ring-2 dark:bg-slate-700 dark:border-slate-600" %> - <% end %> - <%= link_to namespace_project_sample_path(id: sample.id), - data: { - turbo: false - }, - class: "text-slate-900 dark:text-slate-100 font-semibold hover:underline" do %> - - <%= highlight( - sample.puid, - defined?(params[:q][:name_or_puid_cont]) && params[:q][:name_or_puid_cont], - highlighter: '\1' - ) %> - - <% end %> - - <%= link_to namespace_project_sample_path(id: sample.id), - data: { - turbo: false - }, - class: "text-slate-900 dark:text-slate-100 font-semibold hover:underline" do %> - - <%= highlight( - sample.name, - defined?(params[:q][:name_or_puid_cont]) && params[:q][:name_or_puid_cont], - highlighter: '\1' - ) %> - - <% end %> - - <%= l(sample.created_at.localtime, format: :full_date) %> - - <%= viral_time_ago(original_time: sample.updated_at) %> - - <% if sample.attachments_updated_at.present? %> - <%= viral_time_ago(original_time: sample.attachments_updated_at) %> - <% end %> - - <%= sample.metadata[column] %> - - <%= link_to( - t(:"projects.samples.index.edit_button"), - edit_namespace_project_sample_path(id: sample.id), - data: { - turbo: false - }, - class: - "font-medium text-blue-600 underline dark:text-blue-500 hover:no-underline cursor-pointer" - ) %> - <% if allowed_to?(:destroy_sample?, @project) %> - <%= link_to( - t(:"projects.samples.index.remove_button"), - namespace_project_sample_path(id: sample.id), - data: { - turbo_method: :delete, - turbo_confirm: t(:"projects.samples.index.remove_button_confirmation") - }, - class: - "font-medium text-blue-600 underline dark:text-blue-500 hover:no-underline cursor-pointer" - ) %> - <% end %> -
- - - <%= t(".counts.samples") %>: - 0 - - - <%= t(".counts.selected") %>: - 0 - -
- +<%# locals: (project:, pagy:, samples:) -%> +<%= render Samples::TableComponent.new( + samples, + project.namespace, + pagy, + @q, + has_samples: @has_samples, + abilities: { + select_samples: + allowed_to?(:submit_workflow?, project) || + allowed_to?(:clone_sample?, project) || + allowed_to?(:transfer_sample?, project) || + allowed_to?(:export_sample_data?, project), + }, + metadata_fields: @fields, + search_params: @search_params, + row_actions: { + destroy: allowed_to?(:destroy_sample?, project), + edit: allowed_to?(:update_sample?, project), + }, + empty: { + title: t(:"projects.samples.index.no_samples"), + description: t(:"projects.samples.index.no_associated_samples"), + }, +) %> diff --git a/app/views/projects/samples/clones/create.turbo_stream.erb b/app/views/projects/samples/clones/create.turbo_stream.erb index b7a144444f..85bad70ff4 100644 --- a/app/views/projects/samples/clones/create.turbo_stream.erb +++ b/app/views/projects/samples/clones/create.turbo_stream.erb @@ -9,11 +9,11 @@ locals: { type: type, message: message, - errors: errors + errors: errors, } %> <% end %> -<%= turbo_stream.replace "project_samples_list" do %> - <%= turbo_frame_tag "project_samples_list", +<%= turbo_stream.replace "project_samples_table" do %> + <%= turbo_frame_tag "project_samples_table", src: project_samples_path(@project, format: :turbo_stream) %> <% end %> diff --git a/app/views/projects/samples/destroy.turbo_stream.erb b/app/views/projects/samples/destroy.turbo_stream.erb index 53b2ed5100..ab99afb691 100644 --- a/app/views/projects/samples/destroy.turbo_stream.erb +++ b/app/views/projects/samples/destroy.turbo_stream.erb @@ -3,17 +3,11 @@ <% end %> <% if @sample.deleted? %> - <%= turbo_stream.update "project_samples_list", + <%= turbo_stream.update "project_samples_table", partial: "table", locals: { project: @project, - samples: @samples - } %> - - <%= turbo_stream.update "project_samples_pagination", - partial: "pagination", - locals: { - project: @project, - pagy: @pagy + pagy: @pagy, + samples: @samples, } %> <% end %> diff --git a/app/views/projects/samples/index.html.erb b/app/views/projects/samples/index.html.erb index 52203bb9dc..fb89d80026 100644 --- a/app/views/projects/samples/index.html.erb +++ b/app/views/projects/samples/index.html.erb @@ -44,7 +44,7 @@ }, class: "button button--size-default button--state-default action-link" %> <% end %> - <% if allowed_to?(:update_sample?, @project) %> + <% if allowed_to?(:update_sample?, @project) && @has_samples %> <%= link_to t("projects.samples.index.import_metadata_button"), new_namespace_project_samples_file_import_path, data: { @@ -92,9 +92,12 @@ <% end %> - <%= turbo_frame_tag "project_samples_list", - src: project_samples_path(@project, format: :turbo_stream, **request.query_parameters) do %> - <%= render partial: "shared/loading/table" %> - <% end %> - <%= turbo_frame_tag "project_samples_pagination" %> +<% end %> +<%= turbo_frame_tag "project_samples_table" do %> + <%= render partial: "table", + locals: { + project: @project, + samples: @samples, + pagy: @pagy, + } %> <% end %> diff --git a/app/views/projects/samples/index.turbo_stream.erb b/app/views/projects/samples/index.turbo_stream.erb index 1c0e11721a..67b4551cf9 100644 --- a/app/views/projects/samples/index.turbo_stream.erb +++ b/app/views/projects/samples/index.turbo_stream.erb @@ -1,15 +1,9 @@ -<%= turbo_stream.update "project_samples_list", +<%= turbo_stream.update "project_samples_table", partial: "table", locals: { project: @project, - samples: @samples - } %> - -<%= turbo_stream.update "project_samples_pagination", - partial: "pagination", - locals: { - project: @project, - pagy: @pagy + samples: @samples, + pagy: @pagy, } %> <%= turbo_stream.update "project_samples_hidden_values" do %> diff --git a/app/views/projects/samples/show.html.erb b/app/views/projects/samples/show.html.erb index f8aef84ef3..e066a36251 100644 --- a/app/views/projects/samples/show.html.erb +++ b/app/views/projects/samples/show.html.erb @@ -9,7 +9,7 @@ <%= link_to( t("projects.samples.show.edit_button"), edit_namespace_project_sample_path(id: @sample.id), - class: "button button--state-primary button--size-default mr-1" + class: "button button--state-primary button--size-default mr-1", ) %> <% end %> <% if allowed_to?(:destroy?, @project) %> @@ -18,9 +18,9 @@ namespace_project_sample_path(id: @sample.id, format: :html), data: { turbo_method: :delete, - turbo_confirm: t("projects.samples.show.remove_button_confirmation") + turbo_confirm: t("projects.samples.show.remove_button_confirmation"), }, - class: "button button--state-default button--size-default" + class: "button button--state-default button--size-default", ) %> <% end %> @@ -30,15 +30,8 @@
@@ -95,18 +88,18 @@ new_namespace_project_sample_attachment_path(sample_id: @sample.id), data: { turbo_frame: "sample_modal", - turbo_stream: true + turbo_stream: true, }, class: "button button--size-default button--state-default" %> <%= link_to t(".concatenate_button"), new_namespace_project_sample_attachments_concatenation_path( - sample_id: @sample.id + sample_id: @sample.id, ), data: { turbo_frame: "sample_modal", turbo_stream: false, controller: "action-link", - action_link_required_value: 2 + action_link_required_value: 2, }, class: "button button--size-default button--state-default action-link" %> <%= link_to t(".delete_files_button"), @@ -115,7 +108,7 @@ turbo_frame: "sample_modal", turbo_stream: false, controller: "action-link", - action_link_required_value: 1 + action_link_required_value: 1, }, class: "button button--size-default button--state-destructive action-link" %>
@@ -125,18 +118,16 @@ new_namespace_project_sample_metadata_path(sample_id: @sample.id), data: { turbo_frame: "sample_modal", - turbo_stream: true + turbo_stream: true, }, class: "button button--size-default button--state-default" %> - <%= link_to t(".delete_metadata_button"), - new_namespace_project_sample_metadata_deletion_path( - sample_id: @sample.id - ), + <%= link_to t(".delete_metadata_button"), + new_namespace_project_sample_metadata_deletion_path(sample_id: @sample.id), data: { turbo_frame: "sample_modal", turbo_stream: false, controller: "action-link", - action_link_required_value: 1 + action_link_required_value: 1, }, class: "button button--size-default button--state-destructive action-link" %>
@@ -144,7 +135,7 @@ <% end %>
- <%= turbo_frame_tag "table-listing", src: namespace_project_sample_path(format: :turbo_stream, tab: @tab || 'files'), loading: :lazy do %> + <%= turbo_frame_tag "table-listing", src: namespace_project_sample_path(format: :turbo_stream, tab: @tab || 'files') do %> <% if @tab == "history" %> <%= render partial: "shared/loading/history" %> <% elsif @tab == "params" %> diff --git a/app/views/projects/samples/transfers/create.turbo_stream.erb b/app/views/projects/samples/transfers/create.turbo_stream.erb index c43f83635c..2d88d7bc3f 100644 --- a/app/views/projects/samples/transfers/create.turbo_stream.erb +++ b/app/views/projects/samples/transfers/create.turbo_stream.erb @@ -14,7 +14,7 @@ } %> <% end %> -<%= turbo_stream.replace "project_samples_list" do %> - <%= turbo_frame_tag "project_samples_list", +<%= turbo_stream.replace "project_samples_table" do %> + <%= turbo_frame_tag "project_samples_table", src: project_samples_path(@project, format: :turbo_stream) %> <% end %> diff --git a/app/views/workflow_executions/index.html.erb b/app/views/workflow_executions/index.html.erb index d5f0afcdbf..4509957bb3 100644 --- a/app/views/workflow_executions/index.html.erb +++ b/app/views/workflow_executions/index.html.erb @@ -1,7 +1,6 @@ <%= render Viral::PageHeaderComponent.new(title: t(".title")) %> <%= turbo_frame_tag "workflow_execution_table", - src: workflow_executions_path(format: :turbo_stream, page: params[:page]), - loading: :lazy do %> + src: workflow_executions_path(format: :turbo_stream, page: params[:page]) do %> <%= render partial: "shared/loading/table" %> <% end %> <% unless @workflows.empty? %> diff --git a/config/environments/test.rb b/config/environments/test.rb index 03ed2c0b34..0da09c83b6 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -64,4 +64,6 @@ # Annotate rendered view with file names. # config.action_view.annotate_rendered_view_with_filenames = true + + config.middleware.insert_before 0, Capybara::Lockstep::Middleware end diff --git a/config/locales/en.yml b/config/locales/en.yml index e7dc5d5bd8..265355cf1e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -784,6 +784,8 @@ en: counts: samples: Samples selected: Selected + no_samples: No Samples + no_associated_samples: There are no samples associated with this group. models: sample: analysis: Analysis @@ -1160,6 +1162,8 @@ en: remove: Remove sample search: placeholder: Filter by ID or name + no_samples: No Samples + no_associated_samples: There are no samples associated with this project. update: success: Sample was successfully updated. edit: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 472a8bac23..550a9dc049 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,43 +1,44 @@ -lockfileVersion: 5.4 +lockfileVersion: '9.0' -specifiers: - axe-core: ^4.6.3 - flowbite: ^1.7.0 - prettier: ^3.0.0 - prettier-plugin-tailwindcss: ^0.3.0 +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false -dependencies: - flowbite: 1.7.0 +importers: -devDependencies: - axe-core: 4.6.3 - prettier: 3.0.0 - prettier-plugin-tailwindcss: 0.3.0_prettier@3.0.0 + .: + dependencies: + flowbite: + specifier: ^1.7.0 + version: 1.8.1 + devDependencies: + axe-core: + specifier: ^4.6.3 + version: 4.9.1 + prettier: + specifier: ^3.0.0 + version: 3.3.2 + prettier-plugin-tailwindcss: + specifier: ^0.3.0 + version: 0.3.0(prettier@3.3.2) packages: - /@popperjs/core/2.11.6: - resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==} - dev: false + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - /axe-core/4.6.3: - resolution: {integrity: sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==} + axe-core@4.9.1: + resolution: {integrity: sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==} engines: {node: '>=4'} - dev: true - /flowbite/1.7.0: - resolution: {integrity: sha512-OTTmnhRgv85Rs+mcMaVU7zB6EvRQs7BaQziyMUsZLRjW9aUpeQyqKjLmxsVMMCdr8isYPCLd6UL7X1IaSVI0WQ==} - dependencies: - '@popperjs/core': 2.11.6 - mini-svg-data-uri: 1.4.4 - dev: false + flowbite@1.8.1: + resolution: {integrity: sha512-lXTcO8a6dRTPFpINyOLcATCN/pK1Of/jY4PryklPllAiqH64tSDUsOdQpar3TO59ZXWwugm2e92oaqwH6X90Xg==} - /mini-svg-data-uri/1.4.4: + mini-svg-data-uri@1.4.4: resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true - dev: false - /prettier-plugin-tailwindcss/0.3.0_prettier@3.0.0: + prettier-plugin-tailwindcss@0.3.0: resolution: {integrity: sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==} engines: {node: '>=12.17.0'} peerDependencies: @@ -88,12 +89,27 @@ packages: optional: true prettier-plugin-twig-melody: optional: true - dependencies: - prettier: 3.0.0 - dev: true - /prettier/3.0.0: - resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} + prettier@3.3.2: + resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==} engines: {node: '>=14'} hasBin: true - dev: true + +snapshots: + + '@popperjs/core@2.11.8': {} + + axe-core@4.9.1: {} + + flowbite@1.8.1: + dependencies: + '@popperjs/core': 2.11.8 + mini-svg-data-uri: 1.4.4 + + mini-svg-data-uri@1.4.4: {} + + prettier-plugin-tailwindcss@0.3.0(prettier@3.3.2): + dependencies: + prettier: 3.3.2 + + prettier@3.3.2: {} diff --git a/test/fixtures/groups.yml b/test/fixtures/groups.yml index a33253bf9a..0448179f44 100644 --- a/test/fixtures/groups.yml +++ b/test/fixtures/groups.yml @@ -1,232 +1,212 @@ # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html +DEFAULTS: &DEFAULTS + type: Group + metadata_summary: {} + group_one: + <<: *DEFAULTS name: Group 1 path: group-1 - type: Group description: Group 1 description owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> metadata_summary: { "metadatafield1": 633, "metadatafield2": 106 } puid: INXT_GRP_AAAAAAAAAA subgroup1: + <<: *DEFAULTS name: Subgroup 1 path: subgroup-1 - type: Group description: Subgroup 1 description parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAB <% (Namespace::MAX_ANCESTORS-1).times do |n| %> subgroup<%= (n+2) %>: + <<: *DEFAULTS name: <%= "Subgroup #{n+2}" %> path: <%= "subgroup-#{n+2}" %> - type: Group description: <%= "Subgroup #{n+2} description" %> parent_id: <%= ActiveRecord::FixtureSet.identify("subgroup#{n+1}", :uuid) %> - metadata_summary: {} puid: <%= "INXT_GRP_AAAAAA#{((n/26).round+66).chr}AA#{((n%26)+65).chr}" %> <% end %> group_two: + <<: *DEFAULTS name: Group 2 path: group-2 - type: Group description: Group 2 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAC group_three: + <<: *DEFAULTS name: Group 3 path: group-3 - type: Group description: Group 3 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAD subgroup_one_group_three: + <<: *DEFAULTS name: Subgroup 1 Group 3 path: subgroup-1-group-3 - type: Group description: Subgroup 1 Group 3 parent_id: <%= ActiveRecord::FixtureSet.identify(:group_three, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAE david_doe_group_four: + <<: *DEFAULTS name: Group 4 path: group-4 - type: Group description: David's first group - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAF group_five: + <<: *DEFAULTS name: Group 5 path: group-5 - type: Group description: Group 5 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAG subgroup_one_group_five: + <<: *DEFAULTS name: Subgroup 1 Group 5 path: subgroup-1-group-5 - type: Group description: Subgroup 1 Group 5 parent_id: <%= ActiveRecord::FixtureSet.identify(:group_five, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAH group_six: + <<: *DEFAULTS name: Group 6 path: group-6 - type: Group description: Group 6 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAI subgroup_one_group_six: + <<: *DEFAULTS name: Group 6 path: group-6 - type: Group description: Subgroup 1 description parent_id: <%= ActiveRecord::FixtureSet.identify(:group_six, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAJ group_seven: + <<: *DEFAULTS name: Group 7 path: group-7 - type: Group description: Group 7 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAK group_eight: + <<: *DEFAULTS name: Group 8 path: group-8 - type: Group description: Group 8 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAL <% [*("a".."z")].each_with_index do |letter, index| %> group_<%= letter %>: + <<: *DEFAULTS name: <%= "Group #{letter.capitalize}" %> path: <%= "group-#{letter}" %> - type: Group description: <%= "Group #{letter} description" %> created_at: <%= (index + 1).days.ago %> updated_at: <%= (index + 1).days.ago %> - metadata_summary: {} puid: <%= "INXT_GRP_AAAAAAABA#{letter.capitalize}" %> <% end %> namespace_group_link_group_one: + <<: *DEFAULTS name: Group One path: group-one - type: Group description: Group One description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAM namespace_group_link_group_two: + <<: *DEFAULTS name: Group Two path: group-two - type: Group description: Group Two description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAN namespace_group_link_group_three: + <<: *DEFAULTS name: Group Three path: group-three - type: Group description: Group Three description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAO group_nine: + <<: *DEFAULTS name: Group 9 path: group-9 - type: Group description: Group 9 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAP subgroup_one_group_nine: + <<: *DEFAULTS name: Subgroup 1 Group 9 path: subgroup-1-group-9 - type: Group description: Subgroup 1 Group 9 description parent_id: <%= ActiveRecord::FixtureSet.identify(:group_nine, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAQ group_ten: + <<: *DEFAULTS name: Group 10 path: group-10-subgroup-1-group-9 - type: Group description: Group 10 description parent_id: <%= ActiveRecord::FixtureSet.identify(:subgroup_one_group_nine, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAR group_alpha: + <<: *DEFAULTS name: Group Alpha path: group-alpha - type: Group description: Group Alpha description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAS group_bravo: + <<: *DEFAULTS name: Group Bravo path: group-bravo - type: Group description: Group bravo description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAT group_charlie: + <<: *DEFAULTS name: Group Charlie path: group-charlie - type: Group description: Group Charlie description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAU group_alpha_subgroup1: + <<: *DEFAULTS name: Subgroup 1 path: group-alpha/subgroup-1 - type: Group description: Subgroup 1 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAV group_eleven: + <<: *DEFAULTS name: Group 11 path: group-11 - type: Group description: Group 11 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAAW group_twelve: + <<: *DEFAULTS name: Group 12 path: group-12 - type: Group description: Group 12 description owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> metadata_summary: { "metadatafield1": 3, "metadatafield2": 3 } puid: INXT_GRP_AAAAAAAAAX subgroup_twelve_a: + <<: *DEFAULTS name: Subgroup 12 A path: subgroup-12-a - type: Group description: Subgroup 12 A description owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> parent_id: <%= ActiveRecord::FixtureSet.identify(:group_twelve, :uuid) %> @@ -234,9 +214,9 @@ subgroup_twelve_a: puid: INXT_GRP_AAAAAAAAAY subgroup_twelve_b: + <<: *DEFAULTS name: Subgroup 12 B path: subgroup-12-b - type: Group description: Subgroup 12 B description owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> parent_id: <%= ActiveRecord::FixtureSet.identify(:group_twelve, :uuid) %> @@ -244,9 +224,9 @@ subgroup_twelve_b: puid: INXT_GRP_AAAAAAAAAZ subgroup_twelve_a_a: + <<: *DEFAULTS name: Subgroup 12 A A path: subgroup-12-a-a - type: Group description: Subgroup 12 A A description owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> parent_id: <%= ActiveRecord::FixtureSet.identify(:subgroup_twelve_a, :uuid) %> @@ -254,120 +234,114 @@ subgroup_twelve_a_a: puid: INXT_GRP_AAAAAAAAA2 group_delta: + <<: *DEFAULTS name: Group Delta path: group-delta - type: Group description: Group Delta description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAA3 group_delta_subgroupA: + <<: *DEFAULTS name: Subgroup A path: group-delta/subgroup-a - type: Group description: Subgroup A description parent_id: <%= ActiveRecord::FixtureSet.identify(:group_delta, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAAA4 group_echo: + <<: *DEFAULTS name: Group Echo path: group-echo - type: Group description: Group Echo description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAA5 group_echo_subgroupB: + <<: *DEFAULTS name: Subgroup B path: group-echo/subgroup-b - type: Group description: Subgroup B description parent_id: <%= ActiveRecord::FixtureSet.identify(:group_echo, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAAA6 group_foxtrot: + <<: *DEFAULTS name: Group Foxtrot path: group-foxtrot - type: Group description: Group Foxtrot description - metadata_summary: {} puid: INXT_GRP_AAAAAAAAA7 group_foxtrot_subgroupA: + <<: *DEFAULTS name: Subgroup A path: group-foxtrot/subgroup-a - type: Group description: Subgroup A description parent_id: <%= ActiveRecord::FixtureSet.identify(:group_foxtrot, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAABA group_golf: + <<: *DEFAULTS name: Group Golf path: group-golf - type: Group description: Group Golf description - metadata_summary: {} puid: INXT_GRP_AAAAAAAABB group_hotel: + <<: *DEFAULTS name: Group Hotel path: group-hotel - type: Group description: Group Hotel description - metadata_summary: {} owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> puid: INXT_GRP_AAAAAAAABC group_thirteen: + <<: *DEFAULTS name: Group 13 path: group-13 - type: Group description: Group 13 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAABD group_fourteen: + <<: *DEFAULTS name: Group 14 path: group-14 - type: Group description: Group 14 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAABE group_fifteen: + <<: *DEFAULTS name: Group 15 path: group-15 - type: Group description: Group 15 description - metadata_summary: {} puid: INXT_GRP_AAAAAAAABF group_sixteen: + <<: *DEFAULTS name: Group 16 path: group-16 - type: Group description: Group 16 description - metadata_summary: { } owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> puid: INXT_GRP_AAAAAAAABG user30_group_one: + <<: *DEFAULTS name: User 30 Group 1 path: user-30-group-1 - type: Group description: Group 1 description owner_id: <%= ActiveRecord::FixtureSet.identify(:user30, :uuid) %> - metadata_summary: {} puid: INXT_GRP_AAAAAAAABH janitor_doe_group: + <<: *DEFAULTS name: Group EndToEnd path: group-end_to_end - type: Group description: Group 1 description owner_id: <%= ActiveRecord::FixtureSet.identify(:janitor_doe, :uuid) %> - metadata_summary: {} puid: INXT_GRP_ABAAAAAAAA + +empty_group: + <<: *DEFAULTS + name: Empty Group + path: empty-group + description: Group without any Samples + owner_id: <%= ActiveRecord::FixtureSet.identify(:empty_doe, :uuid) %> + puid: INXT_GRP_AAAAAAAABHI diff --git a/test/fixtures/members.yml b/test/fixtures/members.yml index 16b3298dfb..6ee00f2b43 100644 --- a/test/fixtures/members.yml +++ b/test/fixtures/members.yml @@ -541,3 +541,9 @@ project_jeff_member_user_bot_account: created_by_id: <%= ActiveRecord::FixtureSet.identify(:jeff_doe, :uuid) %> namespace_id: <%= ActiveRecord::FixtureSet.identify(:project_jeff_namespace, :uuid) %> access_level: <%= Member::AccessLevel::UPLOADER %> + +empty_group_member_empty_doe: + user_id: <%= ActiveRecord::FixtureSet.identify(:empty_doe, :uuid) %> + created_by_id: <%= ActiveRecord::FixtureSet.identify(:empty_doe, :uuid) %> + namespace_id: <%= ActiveRecord::FixtureSet.identify(:empty_group, :uuid) %> + access_level: <%= Member::AccessLevel::OWNER %> diff --git a/test/fixtures/namespaces/project_namespaces.yml b/test/fixtures/namespaces/project_namespaces.yml index eebe931856..68ead80289 100644 --- a/test/fixtures/namespaces/project_namespaces.yml +++ b/test/fixtures/namespaces/project_namespaces.yml @@ -1,375 +1,348 @@ # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html +DEFAULTS: &DEFAULTS + type: Project + metadata_summary: {} + project1_namespace: + <<: *DEFAULTS name: Project 1 path: project-1 description: Project 1 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> metadata_summary: { "metadatafield1": 10, "metadatafield2": 35 } puid: INXT_PRJ_AAAAAAAAAA project2_namespace: + <<: *DEFAULTS name: Project 2 path: project-2 description: Project 2 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAB john_doe_project2_namespace: + <<: *DEFAULTS name: Project 2 path: project-2 description: Project 2 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:john_doe_namespace, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAC john_doe_project3_namespace: + <<: *DEFAULTS name: Project 3 path: project-3 description: Project 3 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:john_doe_namespace, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAD project4_namespace: + <<: *DEFAULTS name: Project 4 path: project-4 description: Project 4 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:subgroup_one_group_three, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAE project5_namespace: + <<: *DEFAULTS name: Project 5 path: project-5 description: Project 5 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> metadata_summary: { "metadatafield1": 623, "metadatafield2": 71 } puid: INXT_PRJ_AAAAAAAAAF project6_namespace: + <<: *DEFAULTS name: Project 6 path: project-6 description: Project 6 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAG project7_namespace: + <<: *DEFAULTS name: Project 7 path: project-7 description: Project 7 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAH project8_namespace: + <<: *DEFAULTS name: Project 8 path: project-8 description: Project 8 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAI project9_namespace: + <<: *DEFAULTS name: Project 9 path: project-9 description: Project 9 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAJ project10_namespace: + <<: *DEFAULTS name: Project 10 path: project-10 description: Project 10 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAK project11_namespace: + <<: *DEFAULTS name: Project 11 path: project-11 description: Project 11 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAL project12_namespace: + <<: *DEFAULTS name: Project 12 path: project-12 description: Project 12 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAM project13_namespace: + <<: *DEFAULTS name: Project 13 path: project-13 description: Project 13 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAN project14_namespace: + <<: *DEFAULTS name: Project 14 path: project-14 description: Project 14 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAO project15_namespace: + <<: *DEFAULTS name: Project 15 path: project-15 description: Project 15 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAP project16_namespace: + <<: *DEFAULTS name: Project 16 path: project-16 description: Project 16 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAQ project17_namespace: + <<: *DEFAULTS name: Project 17 path: project-17 description: Project 17 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAR project18_namespace: + <<: *DEFAULTS name: Project 18 path: project-18 description: Project 18 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAS project19_namespace: + <<: *DEFAULTS name: Project 19 path: project-19 description: Project 19 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAT project20_namespace: + <<: *DEFAULTS name: Project 20 path: project-20 description: Project 20 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAU project21_namespace: + <<: *DEFAULTS name: Project 21 path: project-21 description: Project 21 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAV project22_namespace: + <<: *DEFAULTS name: Project 22 path: project-22 description: Project 22 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_five, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAW project23_namespace: + <<: *DEFAULTS name: Project 23 path: project-23 description: Project 23 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_six, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAX project24_namespace: + <<: *DEFAULTS name: Project 24 path: project-24 description: Project 24 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAY john_doe_project4_namespace: + <<: *DEFAULTS name: Project 4 path: project-4 description: Project 4 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:john_doe_namespace, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAAZ project25_namespace: + <<: *DEFAULTS name: Project 25 path: project-25 description: Project 25 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:subgroup1, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAA2 project26_namespace: + <<: *DEFAULTS name: Project 26 path: project-26 description: Project 26 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:john_doe_namespace, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAA3 project32_namespace: + <<: *DEFAULTS name: Project 32 path: project-32 description: Project 32 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_eight, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAA4 projectA_namespace: + <<: *DEFAULTS name: Project A path: project-a description: Project A description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:jeff_doe_namespace, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:jeff_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAA5 namespace_group_link_group_one_project1_namespace: + <<: *DEFAULTS name: Group One Project 1 path: project-1 description: Group One Project 1 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:namespace_group_link_group_one, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:user24, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAA6 namespace_group_link_group_three_project1_namespace: + <<: *DEFAULTS name: Group Three Project 1 path: project-1 description: Group Three Project 1 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:namespace_group_link_group_three, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:user24, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAAA7 project28_namespace: + <<: *DEFAULTS name: Project 28 path: project-28 description: Project 28 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:david_doe_group_four, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:david_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABA project29_namespace: + <<: *DEFAULTS name: Project 29 path: project-29 description: Project 29 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:subgroup_twelve_a, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> metadata_summary: { "metadatafield1": 1, "metadatafield2": 1 } puid: INXT_PRJ_AAAAAAAABB project30_namespace: + <<: *DEFAULTS name: Project 30 path: project-30 description: Project 30 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:subgroup_twelve_b, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> metadata_summary: { "metadatafield1": 1, "metadatafield2": 1 } puid: INXT_PRJ_AAAAAAAABC project31_namespace: + <<: *DEFAULTS name: Project 31 path: project-31 description: Project 31 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:subgroup_twelve_a_a, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> metadata_summary: { "metadatafield1": 1, "metadatafield2": 1 } puid: INXT_PRJ_AAAAAAAABD projectAlpha_namespace: + <<: *DEFAULTS name: Project Alpha path: project-alpha description: Project Alpha description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_alpha, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABE projectBravo_namespace: + <<: *DEFAULTS name: Project Bravo path: project-bravo description: Project Bravo description @@ -379,152 +352,144 @@ projectBravo_namespace: puid: INXT_PRJ_AAAAAAAABF projectCharlie_namespace: + <<: *DEFAULTS name: Project Charlie path: project-charlie description: Project Charlie description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_charlie, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABG projectAlpha1_namespace: + <<: *DEFAULTS name: Project Alpha 1 path: project-alpha-1 description: Project Alpha 1 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_alpha_subgroup1, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:private_ryan, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABH projectDelta_namespace: + <<: *DEFAULTS name: Project Delta path: project-delta description: Project Delta description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_delta, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABI projectDeltaSubgroupA_namespace: + <<: *DEFAULTS name: Project Delta Subgroup A path: project-delta-subgroup-a description: Project Delta Subgroup A description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_delta_subgroupA, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABJ projectEcho_namespace: + <<: *DEFAULTS name: Project Echo path: project-echo description: Project Echo description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_echo, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABK projectEchoSubgroupB_namespace: + <<: *DEFAULTS name: Project Echo Subgroup B path: project-echo-subgroup-b description: Project Echo Subgroup B description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_echo_subgroupB, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABL projectFoxtrotSubgroupA_namespace: + <<: *DEFAULTS name: Project Foxtrot Subgroup A path: project-foxtrot-subgroup-a description: Project Foxtrot Subgroup A description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_foxtrot_subgroupA, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABM user27_project1_namespace: + <<: *DEFAULTS name: Personal Project 1 path: personal-project-1 description: Personal Project 1 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:user27_namespace, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:user27, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABN projectHotel_namespace: + <<: *DEFAULTS name: Project Hotel path: project-hotel description: Project Hotel description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_hotel, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABO project_jeff_namespace: + <<: *DEFAULTS name: Project Uncounted path: project-uncounted description: Project Uncounted description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:jeff_doe_namespace, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:jeff_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABP project33_namespace: + <<: *DEFAULTS name: Project 33 path: project-33 description: Project 33 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_thirteen, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:jane_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABQ project34_namespace: + <<: *DEFAULTS name: Project 34 path: project-34 description: Project 34 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_fourteen, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABR project35_namespace: + <<: *DEFAULTS name: Project 35 path: project-35 description: Project 35 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_fifteen, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAAABS project37_namespace: + <<: *DEFAULTS name: Project 37 path: project-37 description: Project 37 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:group_sixteen, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAACBC user29_project1_namespace: + <<: *DEFAULTS name: User 29 Project 1 path: project-1 description: Project 1 description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:user29_namespace, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_AAAAAAACBD project_janitor_namespace: + <<: *DEFAULTS name: Project EndToEnd path: project-end_to_end description: Project EndToEnd description - type: Project parent_id: <%= ActiveRecord::FixtureSet.identify(:janitor_doe_group, :uuid) %> owner_id: <%= ActiveRecord::FixtureSet.identify(:janitor_doe, :uuid) %> - metadata_summary: {} puid: INXT_PRJ_ABAAAAAAAA + +empty_project_namespace: + <<: *DEFAULTS + name: Empty Project + path: empty-project + description: Project that does not contain any samples + parent_id: <%= ActiveRecord::FixtureSet.identify(:empty_group, :uuid) %> + puid: INXT_PRJ_AAAAAAACBE diff --git a/test/fixtures/projects.yml b/test/fixtures/projects.yml index 56677447a3..da9ea738b3 100644 --- a/test/fixtures/projects.yml +++ b/test/fixtures/projects.yml @@ -223,3 +223,7 @@ project_janitor_end_to_end: project_janitor_DELETE: creator_id: <%= ActiveRecord::FixtureSet.identify(:janitor_doe, :uuid) %> namespace_id: <%= ActiveRecord::FixtureSet.identify(:project_janitor_namespace, :uuid) %> + +empty_project: + creator_id: <%= ActiveRecord::FixtureSet.identify(:john_doe, :uuid) %> + namespace_id: <%= ActiveRecord::FixtureSet.identify(:empty_project_namespace, :uuid) %> diff --git a/test/fixtures/routes.yml b/test/fixtures/routes.yml index b2bfec7c57..d8e9c7a6e8 100644 --- a/test/fixtures/routes.yml +++ b/test/fixtures/routes.yml @@ -479,3 +479,13 @@ user30_group_one_route: name: User 30 Group 1 path: user-30-group-1 source: user30_group_one (Namespace) + +empty_group_route: + name: Empty Group + path: empty-group + source: empty_group (Namespace) + +empty_project_namespace_route: + name: Empty Group / Empty Project + path: empty-group/empty-project + source: empty_project_namespace (Namespace) diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index ded98202ba..249557e657 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -1,220 +1,208 @@ # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html +DEFAULTS: &DEFAULTS + encrypted_password: <%= User.new.send :password_digest, "password1" %> + locale: en + user_type: human + john_doe: + <<: *DEFAULTS email: john.doe@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: John last_name: Doe - locale: en jane_doe: + <<: *DEFAULTS email: jane.doe@localhost first_name: Jane last_name: Doe - locale: en jean_doe: + <<: *DEFAULTS email: jean.doe@localhost first_name: Jean last_name: Doe - locale: en james_doe: + <<: *DEFAULTS email: james.doe@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: James last_name: Doe locale: fr joan_doe: + <<: *DEFAULTS email: joan.doe@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Joan last_name: Doe locale: fr steve_doe: + <<: *DEFAULTS email: steve.doe@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Steve last_name: Doe - locale: en michelle_doe: + <<: *DEFAULTS email: michelle.doe@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Michelle last_name: Doe - locale: en micha_doe: + <<: *DEFAULTS email: micha.doe@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Micha last_name: Doe - locale: en ryan_doe: + <<: *DEFAULTS email: ryan.doe@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Ryan last_name: Doe - locale: en david_doe: + <<: *DEFAULTS email: david.doe@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: David last_name: Doe - locale: en jeff_doe: + <<: *DEFAULTS email: jeff.doe@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Jeff last_name: Doe provider: developer uid: jeff.doe@localhost - locale: en alph_abet: + <<: *DEFAULTS email: alph.abet@localhost - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Alph last_name: Abet - locale: en <% 25.times do |n| %> user<%= (n) %>: + <<: *DEFAULTS email: <%= "user#{n}@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: User last_name: <%= n %> - locale: en <% end %> user25: + <<: *DEFAULTS email: <%= "user25@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: User last_name: "25" - locale: en # Do not link this user to anything user_no_access: + <<: *DEFAULTS email: <%= "user_no_access@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: User last_name: "No Access" - locale: en user_no_access: + <<: *DEFAULTS email: <%= "user_no_access@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: User last_name: "No Access" - locale: en private_ryan: + <<: *DEFAULTS email: <%= "private.ryan@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Private last_name: Ryan - locale: en user26: + <<: *DEFAULTS email: <%= "user26@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: User last_name: "26" - locale: en private_joan: + <<: *DEFAULTS email: <%= "private.joan@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Private last_name: Joan - locale: en private_micha: + <<: *DEFAULTS email: <%= "private.micha@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Private last_name: Micha - locale: en user27: + <<: *DEFAULTS email: <%= "user27@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: User last_name: "27" - locale: en user28: + <<: *DEFAULTS email: <%= "user28@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: User last_name: "28" - locale: en <% 22.times do |n| %> user_bot_account<%= (n) %>: + <<: *DEFAULTS email: <%= "inxt_prj_aaaaaaaaaa_bot_#{format('%03d', (n + 1))}@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Project last_name: "Bot #{format('%03d', (n + 1))}" user_type: <%= User.user_types[:project_bot] %> - locale: en <% end %> user29: + <<: *DEFAULTS email: <%= "user29@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: User last_name: "29" - locale: en user30: + <<: *DEFAULTS email: <%= "user30@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: User last_name: "30" - locale: en <% 22.times do |n| %> user_group_bot_account<%= (n) %>: + <<: *DEFAULTS email: <%= "inxt_grp_aaaaaaaaaa_bot#{format('%03d', (n + 1))}@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: Group last_name: "Bot #{format('%03d', (n + 1))}" user_type: <%= User.user_types[:group_bot] %> - locale: en <% end %> project1_automation_bot: + <<: *DEFAULTS email: <%= "inxt_prj_aaaaaaaaaa_automation_bot@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: INXT_PRJ_AAAAAAAAAA last_name: Automation Bot user_type: <%= User.user_types[:project_automation_bot] %> projectA_automation_bot: + <<: *DEFAULTS email: <%= "inxt_prj_aaaaaaaaa5_automation_bot@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: INXT_PRJ_AAAAAAAAA5 last_name: Automation Bot user_type: <%= User.user_types[:project_automation_bot] %> janitor_doe: + <<: *DEFAULTS email: <%= "janitor_doe@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: project last_name: "end to end user" - locale: en projectJeff_bot: + <<: *DEFAULTS email: <%= "inxt_prj_aaaaaaaabp_001@localhost" %> - encrypted_password: <%= User.new.send :password_digest, "password1" %> first_name: INXT_PRJ_AAAAAAAABP last_name: Bot user_type: <%= User.user_types[:project_bot] %> + +empty_doe: + <<: *DEFAULTS + email: <%= "empty_doe@localhost" %> + first_name: Empty + last_name: Doe diff --git a/test/system/groups/samples_test.rb b/test/system/groups/samples_test.rb index a185db5c1a..3c08979d8b 100644 --- a/test/system/groups/samples_test.rb +++ b/test/system/groups/samples_test.rb @@ -18,9 +18,9 @@ def setup def retrieve_puids puids = [] - within first('table tbody#group-samples-table-body') do + within first('table tbody') do (1..4).each do |n| - puids << first("tr:nth-child(#{n}) td:first-child").text + puids << first("tr:nth-child(#{n}) th").text end end puids @@ -100,18 +100,14 @@ def retrieve_puids visit group_samples_url(@group) assert_text 'Displaying items 1-20 of 26 in total' - within '#group_samples_list' do - assert_selector 'table tbody tr', count: 20 - assert_text @sample1.name - assert_text @sample2.name - end + assert_selector 'table tbody tr', count: 20 + assert_text @sample1.name + assert_text @sample2.name fill_in placeholder: I18n.t(:'groups.samples.index.search.placeholder'), with: 'Sample 1' - within '#group_samples_list' do - assert_selector 'tfoot strong[data-selection-target="total"]', text: '13' - assert_selector 'table tbody tr', count: 13 - end + assert_text 'Samples: 13' + assert_selector 'table tbody tr', count: 13 assert_text @sample1.name assert_no_text @sample2.name @@ -124,7 +120,7 @@ def retrieve_puids # Because PUIDs are not always generated the same, issues regarding order have occurred when hard testing # the expected ordering of samples based on PUID. To resolve this, we will gather the first 4 PUIDs and ensure # they are ordered as expected against one another. - assert_selector 'table tbody#group-samples-table-body tr', count: 20 + assert_selector 'table tbody tr', count: 20 click_on I18n.t('groups.samples.table.puid') assert_selector 'table thead th:first-child svg.icon-arrow_up' @@ -142,28 +138,28 @@ def retrieve_puids click_on I18n.t('groups.samples.table.sample') assert_selector 'table thead th:nth-child(2) svg.icon-arrow_up' - within first('table tbody#group-samples-table-body') do - assert_selector 'tr:first-child td:first-child', text: @sample1.puid + within first('table tbody') do + assert_selector 'tr:first-child th', text: @sample1.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample1.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name end click_on I18n.t('groups.samples.table.created_at') assert_selector 'table thead th:nth-child(4) svg.icon-arrow_up' - within first('table tbody#group-samples-table-body') do - assert_selector 'tr:nth-child(3) td:first-child', text: @sample28.puid + within first('table tbody') do + assert_selector 'tr:nth-child(3) th', text: @sample28.puid assert_selector 'tr:nth-child(3) td:nth-child(2)', text: @sample28.name - assert_selector 'tr:nth-child(4) td:first-child', text: @sample25.puid + assert_selector 'tr:nth-child(4) th', text: @sample25.puid assert_selector 'tr:nth-child(4) td:nth-child(2)', text: @sample25.name end click_on I18n.t('groups.samples.table.created_at') assert_selector 'table thead th:nth-child(4) svg.icon-arrow_down' - within first('table tbody#group-samples-table-body') do - assert_selector 'tr:first-child td:first-child', text: @sample1.puid + within first('table tbody') do + assert_selector 'tr:first-child th', text: @sample1.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample1.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name end end @@ -172,19 +168,15 @@ def retrieve_puids visit group_samples_url(@group) assert_text 'Displaying items 1-20 of 26 in total' - within '#group_samples_list' do - assert_selector 'table tbody tr', count: 20 - within first('table tbody tr td') do - assert_text @sample1.puid - end + assert_selector 'table tbody tr', count: 20 + within first('table tbody tr th') do + assert_text @sample1.puid end fill_in placeholder: I18n.t(:'groups.samples.index.search.placeholder'), with: 'Sample 1' - within('#group_samples_list') do - assert_selector 'tfoot strong[data-selection-target="total"]', text: '13' - assert_selector 'table tbody tr', count: 13 - end + assert_text 'Samples: 13' + assert_selector 'table tbody tr', count: 13 assert_text @sample1.name assert_no_text @sample2.name @@ -193,124 +185,110 @@ def retrieve_puids click_on I18n.t('groups.samples.table.sample') assert_selector 'table thead th:nth-child(2) svg.icon-arrow_up' - within '#group_samples_list' do - assert_selector 'tbody tr:first-child td:first-child', text: @sample1.puid - assert_selector 'tbody tr:first-child td:nth-child(2)', text: @sample1.name - end + assert_selector 'tbody tr:first-child th', text: @sample1.puid + assert_selector 'tbody tr:first-child td:nth-child(2)', text: @sample1.name click_on I18n.t('groups.samples.table.sample') assert_selector 'table thead th:nth-child(2) svg.icon-arrow_down' - within '#group_samples_list' do - assert_selector 'tbody tr:last-child td:first-child', text: @sample1.puid - assert_selector 'tbody tr:last-child td:nth-child(2)', text: @sample1.name - end + assert_selector 'tbody tr:last-child th', text: @sample1.puid + assert_selector 'tbody tr:last-child td:nth-child(2)', text: @sample1.name end test 'can filter by puid and then sort the list of samples' do visit group_samples_url(@group) assert_text 'Displaying items 1-20 of 26 in total' - within '#group_samples_list' do - assert_selector 'table tbody tr', count: 20 - within first('table tbody tr td') do - assert_text @sample1.puid - end + assert_selector 'table tbody tr', count: 20 + within first('table tbody tr th') do + assert_text @sample1.puid end fill_in placeholder: I18n.t(:'groups.samples.index.search.placeholder'), with: @sample1.puid assert_text 'Displaying 1 item' - within '#group_samples_list' do - assert_selector 'table tbody tr', count: 1 - assert_text @sample1.name - assert_no_text @sample2.name - end + assert_selector 'table tbody tr', count: 1 + assert_text @sample1.name + assert_no_text @sample2.name click_on I18n.t('groups.samples.table.sample') assert_selector 'table thead th:nth-child(2) svg.icon-arrow_up' - within '#group_samples_list' do - assert_selector 'table tbody tr', count: 1 - assert_selector 'table tbody tr:first-child td:first-child', text: @sample1.puid - assert_selector 'table tbody tr:first-child td:nth-child(2)', text: @sample1.name - end + assert_selector 'table tbody tr', count: 1 + assert_selector 'table tbody tr:first-child th', text: @sample1.puid + assert_selector 'table tbody tr:first-child td:nth-child(2)', text: @sample1.name end test 'can sort and then filter the list of samples by name' do visit group_samples_url(@group) assert_text 'Displaying items 1-20 of 26 in total' - assert_selector 'table tbody#group-samples-table-body tr', count: 20 - within first('table tbody#group-samples-table-body tr td') do + assert_selector 'table tbody tr', count: 20 + within first('table tbody tr th') do assert_text @sample1.puid end click_on I18n.t('groups.samples.table.sample') assert_selector 'table thead th:nth-child(2) svg.icon-arrow_up' within first('table tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample1.puid + assert_selector 'tr:first-child th', text: @sample1.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample1.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name end click_on I18n.t('groups.samples.table.created_at') assert_selector 'table thead th:nth-child(4) svg.icon-arrow_up' within first('table tbody') do - assert_selector 'tr:nth-child(3) td:first-child', text: @sample28.puid + assert_selector 'tr:nth-child(3) th', text: @sample28.puid assert_selector 'tr:nth-child(3) td:nth-child(2)', text: @sample28.name - assert_selector 'tr:nth-child(4) td:first-child', text: @sample25.puid + assert_selector 'tr:nth-child(4) th', text: @sample25.puid assert_selector 'tr:nth-child(4) td:nth-child(2)', text: @sample25.name end fill_in placeholder: I18n.t(:'groups.samples.index.search.placeholder'), with: 'Sample 1' assert_text 'Displaying 13 items' - within '#group_samples_list' do - assert_selector 'table tbody tr', count: 13 - assert_text @sample1.name - assert_no_text @sample2.name - assert_no_text @sample9.name - end + assert_selector 'table tbody tr', count: 13 + assert_text @sample1.name + assert_no_text @sample2.name + assert_no_text @sample9.name end test 'can sort and then filter the list of samples by puid' do visit group_samples_url(@group) assert_text 'Displaying items 1-20 of 26 in total' - assert_selector 'table tbody#group-samples-table-body tr', count: 20 - within first('table tbody#group-samples-table-body tr td') do + assert_selector 'table tbody tr', count: 20 + within first('table tbody tr th') do assert_text @sample1.puid end click_on I18n.t('groups.samples.table.sample') assert_selector 'table thead th:nth-child(2) svg.icon-arrow_up' within first('table tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample1.puid + assert_selector 'tr:first-child th', text: @sample1.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample1.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name end click_on I18n.t('groups.samples.table.created_at') assert_selector 'table thead th:nth-child(4) svg.icon-arrow_up' within first('table tbody') do - assert_selector 'tr:nth-child(3) td:first-child', text: @sample28.puid + assert_selector 'tr:nth-child(3) th', text: @sample28.puid assert_selector 'tr:nth-child(3) td:nth-child(2)', text: @sample28.name - assert_selector 'tr:nth-child(4) td:first-child', text: @sample25.puid + assert_selector 'tr:nth-child(4) th', text: @sample25.puid assert_selector 'tr:nth-child(4) td:nth-child(2)', text: @sample25.name end fill_in placeholder: I18n.t(:'groups.samples.index.search.placeholder'), with: @sample1.puid assert_text 'Displaying 1 item' - within '#group_samples_list' do - assert_selector 'table tbody tr', count: 1 - assert_text @sample1.name - assert_no_text @sample2.name - assert_no_text @sample9.name - end + assert_selector 'table tbody tr', count: 1 + assert_text @sample1.name + assert_no_text @sample2.name + assert_no_text @sample9.name end test 'should be able to toggle metadata' do @@ -341,22 +319,18 @@ def retrieve_puids click_on 'metadatafield1' assert_selector 'table thead th:nth-child(7) svg.icon-arrow_up' - within '#group_samples_list' do - assert_selector 'tbody tr:first-child td:first-child', text: @sample30.puid - assert_selector 'tbody tr:first-child td:nth-child(2)', text: @sample30.name - assert_selector 'tbody tr:nth-child(2) td:first-child', text: @sample2.puid - assert_selector 'tbody tr:nth-child(2) td:nth-child(2)', text: @sample2.name - end + assert_selector 'tbody tr:first-child th', text: @sample30.puid + assert_selector 'tbody tr:first-child td:nth-child(2)', text: @sample30.name + assert_selector 'tbody tr:nth-child(2) th', text: @sample2.puid + assert_selector 'tbody tr:nth-child(2) td:nth-child(2)', text: @sample2.name click_on 'metadatafield2' assert_selector 'table thead th:nth-child(8) svg.icon-arrow_up' - within '#group_samples_list' do - assert_selector 'tbody tr:first-child td:first-child', text: @sample30.puid - assert_selector 'tbody tr:first-child td:nth-child(2)', text: @sample30.name - assert_selector 'tbody tr:nth-child(2) td:first-child', text: @sample2.puid - assert_selector 'tbody tr:nth-child(2) td:nth-child(2)', text: @sample2.name - end + assert_selector 'tbody tr:first-child th', text: @sample30.puid + assert_selector 'tbody tr:first-child td:nth-child(2)', text: @sample30.name + assert_selector 'tbody tr:nth-child(2) th', text: @sample2.puid + assert_selector 'tbody tr:nth-child(2) td:nth-child(2)', text: @sample2.name # toggling metadata again causes sort to be reset find('label', text: I18n.t(:'projects.samples.shared.metadata_toggle.label')).click @@ -364,9 +338,9 @@ def retrieve_puids assert_selector 'table thead th:nth-child(5) svg.icon-arrow_down' within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample1.puid + assert_selector 'tr:first-child th', text: @sample1.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample1.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name end end @@ -374,11 +348,11 @@ def retrieve_puids test 'filtering samples by list of sample puids' do visit group_samples_url(@group) assert_text 'Displaying items 1-20 of 26 in total' - within 'tbody#group-samples-table-body' do + within 'tbody' do assert_selector 'tr', count: 20 - assert_selector 'tr td', text: @sample1.puid - assert_selector 'tr td', text: @sample2.puid - assert_selector 'tr td', text: @sample9.puid + assert_selector 'tr th', text: @sample1.puid + assert_selector 'tr th', text: @sample2.puid + assert_selector 'tr th', text: @sample9.puid end find("button[aria-label='#{I18n.t(:'components.list_filter.title')}").click @@ -391,11 +365,11 @@ def retrieve_puids click_button I18n.t(:'components.list_filter.apply') end - within 'tbody#group-samples-table-body' do + within 'tbody' do assert_selector 'tr', count: 2 - assert_selector 'tr td', text: @sample1.puid - assert_selector 'tr td', text: @sample2.puid - assert_no_selector 'tr td', text: @sample9.puid + assert_selector 'tr th', text: @sample1.puid + assert_selector 'tr th', text: @sample2.puid + assert_no_selector 'tr th', text: @sample9.puid end find("button[aria-label='#{I18n.t(:'components.list_filter.title')}").click @@ -404,7 +378,7 @@ def retrieve_puids click_button I18n.t(:'components.list_filter.clear') click_button I18n.t(:'components.list_filter.apply') end - within 'tbody#group-samples-table-body' do + within 'tbody' do assert_selector 'tr', count: 20 end end @@ -417,7 +391,7 @@ def retrieve_puids assert_selector 'input[name="sample_ids[]"]:checked', count: 0 end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '26' + assert_text 'Samples: 26' assert_selector 'strong[data-selection-target="selected"]', text: '0' end find('input[name="select"]').click @@ -425,14 +399,14 @@ def retrieve_puids assert_selector 'input[name="sample_ids[]"]:checked', count: 20 end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '26' + assert_text 'Samples: 26' assert_selector 'strong[data-selection-target="selected"]', text: '26' end within 'tbody' do first('input[name="sample_ids[]"]').click end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '26' + assert_text 'Samples: 26' assert_selector 'strong[data-selection-target="selected"]', text: '25' end @@ -442,7 +416,7 @@ def retrieve_puids assert_selector 'input[name="sample_ids[]"]:checked', count: 20 end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '26' + assert_text 'Samples: 26' assert_selector 'strong[data-selection-target="selected"]', text: '26' end find('input[name="select"]').click @@ -460,16 +434,14 @@ def retrieve_puids assert_selector 'input[name="sample_ids[]"]:checked', count: 0 end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '26' + assert_text 'Samples: 26' assert_selector 'strong[data-selection-target="selected"]', text: '0' end fill_in placeholder: I18n.t(:'groups.samples.index.search.placeholder'), with: @sample1.name - within '#group_samples_list' do - assert_selector 'strong[data-selection-target="total"]', text: '1' - assert_selector 'table tbody tr', count: 1 - end + assert_text 'Samples: 1' + assert_selector 'table tbody tr', count: 1 within 'tbody' do assert_selector 'input[name="sample_ids[]"]', count: 1 @@ -482,17 +454,15 @@ def retrieve_puids assert_selector 'input[name="sample_ids[]"]:checked', count: 1 end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '1' + assert_text 'Samples: 1' assert_selector 'strong[data-selection-target="selected"]', text: '1' end fill_in placeholder: I18n.t(:'groups.samples.index.search.placeholder'), with: ' ' - within '#group_samples_list' do - assert_selector 'tfoot strong[data-selection-target="total"]', text: '26' - assert_selector 'tfoot strong[data-selection-target="selected"]', text: '0' - assert_selector 'table tbody tr', count: 20 - end + assert_text 'Samples: 26' + assert_selector 'tfoot strong[data-selection-target="selected"]', text: '0' + assert_selector 'table tbody tr', count: 20 end end end diff --git a/test/system/projects/samples_test.rb b/test/system/projects/samples_test.rb index c0eafc8c32..ef3ab9993d 100644 --- a/test/system/projects/samples_test.rb +++ b/test/system/projects/samples_test.rb @@ -21,7 +21,7 @@ class SamplesTest < ApplicationSystemTestCase test 'visiting the index' do visit namespace_project_samples_url(@namespace, @project) assert_selector 'h1', text: I18n.t('projects.samples.index.title') - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 assert_text @sample1.name assert_text @sample2.name end @@ -192,10 +192,10 @@ class SamplesTest < ApplicationSystemTestCase assert_text I18n.t('projects.samples.destroy.success', sample_name: @sample1.name, project_name: @project.namespace.human_name) - assert_no_selector 'table#samples-table tbody tr', text: @sample1.name + assert_no_selector '#samples-table table tbody tr', text: @sample1.name assert_selector 'h1', text: I18n.t(:'projects.samples.index.title'), count: 1 - assert_selector 'table#samples-table tbody tr', count: 2 - within first('tbody tr td:first-child') do + assert_selector '#samples-table table tbody tr', count: 2 + within first('tbody tr th') do assert_text @sample2.puid end end @@ -204,7 +204,7 @@ class SamplesTest < ApplicationSystemTestCase visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - table_row = find(:table_row, { 'Sample' => @sample1.name }) + table_row = find(:table_row, [@sample1.name]) within table_row do click_link 'Remove' @@ -217,11 +217,11 @@ class SamplesTest < ApplicationSystemTestCase assert_text I18n.t('projects.samples.destroy.success', sample_name: @sample1.name, project_name: @project.namespace.human_name) - assert_no_selector 'table#samples-table tbody tr', text: @sample1.puid - assert_no_selector 'table#samples-table tbody tr', text: @sample1.name + assert_no_selector '#samples-table table tbody tr', text: @sample1.puid + assert_no_selector '#samples-table table tbody tr', text: @sample1.name assert_selector 'h1', text: I18n.t(:'projects.samples.index.title'), count: 1 - assert_selector 'table#samples-table tbody tr', count: 2 - within first('tbody tr td:first-child') do + assert_selector '#samples-table table tbody tr', count: 2 + within first('tbody tr th') do assert_text @sample2.puid end end @@ -230,7 +230,7 @@ class SamplesTest < ApplicationSystemTestCase project2 = projects(:project2) visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 all('input[type=checkbox]').each { |checkbox| checkbox.click unless checkbox.checked? } end @@ -252,7 +252,7 @@ class SamplesTest < ApplicationSystemTestCase project2 = projects(:project2) visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 all('input[type=checkbox]').each { |checkbox| checkbox.click unless checkbox.checked? } end @@ -264,17 +264,39 @@ class SamplesTest < ApplicationSystemTestCase end within %(turbo-frame[id="samples_dialog"]) do assert_text I18n.t('projects.samples.transfers.create.no_samples_transferred_error') + assert_no_selector "turbo-frame[id='list_select_samples']" errors = @project.errors.full_messages_for(:samples) errors.each { |error| assert_text error } end end + test 'should not transfer samples' do + project26 = projects(:project26) + visit namespace_project_samples_url(@namespace, @project) + assert_text 'Displaying 3 items' + assert_selector '#samples-table table tbody tr', count: 3 + all('input[type=checkbox]').last.click + + click_link I18n.t('projects.samples.index.transfer_button'), match: :first + within('span[data-controller-connected="true"] dialog') do + select project26.full_path, from: I18n.t('projects.samples.transfers.dialog.new_project_id') + assert_button I18n.t('projects.samples.transfers.dialog.submit_button') + click_on I18n.t('projects.samples.transfers.dialog.submit_button') + end + assert_no_button I18n.t('projects.samples.transfers.dialog.submit_button') + within %(turbo-frame[id="samples_dialog"]) do + assert_text I18n.t('projects.samples.transfers.create.error') + errors = @project.errors.full_messages_for(:samples) + errors.each { |error| assert_text error } + end + end + test 'should transfer some samples' do project25 = projects(:project25) visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 all('input[type=checkbox]').each { |checkbox| checkbox.click unless checkbox.checked? } end @@ -304,7 +326,7 @@ class SamplesTest < ApplicationSystemTestCase project2 = projects(:project2) visit namespace_project_samples_url(namespace_id: @namespace.path, project_id: @project.path) assert_text 'Displaying 3 items' - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 all('input[type=checkbox]').each { |checkbox| checkbox.click unless checkbox.checked? } end @@ -329,7 +351,7 @@ class SamplesTest < ApplicationSystemTestCase project2 = projects(:projectHotel) visit namespace_project_samples_url(namespace, project2) assert_text 'Displaying 1 item' - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 1 all('input[type=checkbox]').each { |checkbox| checkbox.click unless checkbox.checked? } end @@ -354,7 +376,7 @@ class SamplesTest < ApplicationSystemTestCase project32 = projects(:project32) visit namespace_project_samples_url(namespace_id: @namespace.path, project_id: @project.path) assert_text 'Displaying 3 items' - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 all('input[type=checkbox]').each { |checkbox| checkbox.click unless checkbox.checked? } end @@ -425,7 +447,7 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'a', text: I18n.t('projects.samples.index.new_button'), count: 1 assert_selector 'h1', text: I18n.t('projects.samples.index.title') assert_text 'Displaying 3 items' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 within first('tbody tr') do assert_selector 'a', text: 'Edit', count: 1 assert_selector 'a', text: 'Remove', count: 0 @@ -443,7 +465,7 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'a', text: I18n.t('projects.samples.index.new_button'), count: 0 assert_selector 'h1', text: I18n.t('projects.samples.index.title') assert_text 'Displaying 3 items' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 assert_selector 'a', text: 'Edit', count: 0 assert_selector 'a', text: 'Remove', count: 0 assert_text @sample1.name @@ -454,14 +476,14 @@ class SamplesTest < ApplicationSystemTestCase visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 assert_text @sample1.name assert_text @sample2.name fill_in placeholder: I18n.t(:'projects.samples.index.search.placeholder'), with: samples(:sample1).name assert_text 'Displaying 1 item' - assert_selector 'table#samples-table tbody tr', count: 1 + assert_selector '#samples-table table tbody tr', count: 1 assert_text @sample1.name assert_no_text @sample2.name assert_no_text @sample3.name @@ -471,8 +493,8 @@ class SamplesTest < ApplicationSystemTestCase visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - assert_selector 'table#samples-table tbody tr', count: 3 - within first('tbody tr td:first-child') do + assert_selector '#samples-table table tbody tr', count: 3 + within first('tbody tr th') do assert_text @sample1.puid end @@ -494,26 +516,26 @@ class SamplesTest < ApplicationSystemTestCase click_on I18n.t('projects.samples.table.sample') assert_selector 'table thead th:nth-child(2) svg.icon-arrow_up' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample1.puid + assert_selector 'tr:first-child th', text: @sample1.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample1.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name - assert_selector 'tr:last-child td:first-child', text: @sample3.puid + assert_selector 'tr:last-child th', text: @sample3.puid assert_selector 'tr:last-child td:nth-child(2)', text: @sample3.name end click_on I18n.t('projects.samples.table.sample') assert_selector 'table thead th:nth-child(2) svg.icon-arrow_down' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample3.puid + assert_selector 'tr:first-child th', text: @sample3.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample3.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name - assert_selector 'tr:last-child td:first-child', text: @sample1.puid + assert_selector 'tr:last-child th', text: @sample1.puid assert_selector 'tr:last-child td:nth-child(2)', text: @sample1.name end @@ -521,11 +543,11 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'table thead th:nth-child(3) svg.icon-arrow_up' within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample3.puid + assert_selector 'tr:first-child th', text: @sample3.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample3.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name - assert_selector 'tr:last-child td:first-child', text: @sample1.puid + assert_selector 'tr:last-child th', text: @sample1.puid assert_selector 'tr:last-child td:nth-child(2)', text: @sample1.name end @@ -533,11 +555,11 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'table thead th:nth-child(3) svg.icon-arrow_down' within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample1.puid + assert_selector 'tr:first-child th', text: @sample1.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample1.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name - assert_selector 'tr:last-child td:first-child', text: @sample3.puid + assert_selector 'tr:last-child th', text: @sample3.puid assert_selector 'tr:last-child td:nth-child(2)', text: @sample3.name end @@ -545,11 +567,11 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'table thead th:nth-child(4) svg.icon-arrow_up' within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample3.puid + assert_selector 'tr:first-child th', text: @sample3.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample3.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name - assert_selector 'tr:last-child td:first-child', text: @sample1.puid + assert_selector 'tr:last-child th', text: @sample1.puid assert_selector 'tr:last-child td:nth-child(2)', text: @sample1.name end @@ -557,11 +579,11 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'table thead th:nth-child(4) svg.icon-arrow_down' within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample1.puid + assert_selector 'tr:first-child th', text: @sample1.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample1.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name - assert_selector 'tr:last-child td:first-child', text: @sample3.puid + assert_selector 'tr:last-child th', text: @sample3.puid assert_selector 'tr:last-child td:nth-child(2)', text: @sample3.name end end @@ -570,7 +592,7 @@ class SamplesTest < ApplicationSystemTestCase visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 within first('tbody tr td:nth-child(2)') do assert_text @sample1.name end @@ -578,7 +600,7 @@ class SamplesTest < ApplicationSystemTestCase fill_in placeholder: I18n.t(:'projects.samples.index.search.placeholder'), with: samples(:sample1).name assert_text 'Displaying 1 item' - assert_selector 'table#samples-table tbody tr', count: 1 + assert_selector '#samples-table table tbody tr', count: 1 assert_text @sample1.puid assert_text @sample1.name assert_no_text @sample2.name @@ -586,7 +608,7 @@ class SamplesTest < ApplicationSystemTestCase click_on I18n.t('projects.samples.table.sample') - assert_selector 'table#samples-table tbody tr', count: 1 + assert_selector '#samples-table table tbody tr', count: 1 within first('tbody tr td:nth-child(2)') do assert_text @sample1.name end @@ -596,15 +618,15 @@ class SamplesTest < ApplicationSystemTestCase visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - assert_selector 'table#samples-table tbody tr', count: 3 - within first('tbody tr td:first-child') do + assert_selector '#samples-table table tbody tr', count: 3 + within first('tbody tr th') do assert_text @sample1.puid end fill_in placeholder: I18n.t(:'projects.samples.index.search.placeholder'), with: @sample1.puid assert_text 'Displaying 1 item' - assert_selector 'table#samples-table tbody tr', count: 1 + assert_selector '#samples-table table tbody tr', count: 1 assert_text @sample1.puid assert_text @sample1.name assert_no_text @sample2.name @@ -612,8 +634,8 @@ class SamplesTest < ApplicationSystemTestCase click_on I18n.t('projects.samples.table.puid') - assert_selector 'table#samples-table tbody tr', count: 1 - within first('tbody tr td:first-child') do + assert_selector '#samples-table table tbody tr', count: 1 + within first('tbody tr th') do assert_text @sample1.puid end end @@ -622,7 +644,7 @@ class SamplesTest < ApplicationSystemTestCase visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 within first('tbody tr td:nth-child(2)') do assert_text @sample1.name end @@ -632,17 +654,15 @@ class SamplesTest < ApplicationSystemTestCase click_on I18n.t('projects.samples.table.sample') assert_selector 'table thead th:nth-child(2) svg.icon-arrow_down' - within '#project_samples_list' do - assert_selector 'table#samples-table tbody tr', count: 3 - within first('tbody tr td:nth-child(2)') do - assert_text @sample3.name - end + assert_selector '#samples-table table tbody tr', count: 3 + within first('tbody tr td:nth-child(2)') do + assert_text @sample3.name end fill_in placeholder: I18n.t(:'projects.samples.index.search.placeholder'), with: samples(:sample1).name assert_text 'Displaying 1 item' - assert_selector 'table#samples-table tbody tr', count: 1 + assert_selector '#samples-table table tbody tr', count: 1 assert_text @sample1.puid assert_text @sample1.name assert_no_text @sample2.name @@ -653,23 +673,23 @@ class SamplesTest < ApplicationSystemTestCase visit namespace_project_samples_url(@namespace, @project) assert_text 'Displaying 3 items' - assert_selector 'table#samples-table tbody tr', count: 3 - within first('tbody tr td:first-child') do + assert_selector '#samples-table table tbody tr', count: 3 + within first('tbody tr th') do assert_text @sample1.puid end click_on I18n.t('projects.samples.table.sample') click_on I18n.t('projects.samples.table.sample') - assert_selector 'table#samples-table tbody tr', count: 3 - within first('tbody tr td:first-child') do + assert_selector '#samples-table table tbody tr', count: 3 + within first('tbody tr th') do assert_text @sample3.puid end fill_in placeholder: I18n.t(:'projects.samples.index.search.placeholder'), with: @sample1.puid assert_text 'Displaying 1 item' - assert_selector 'table#samples-table tbody tr', count: 1 + assert_selector '#samples-table table tbody tr', count: 1 assert_text @sample1.puid assert_text @sample1.name assert_no_text @sample2.name @@ -1158,24 +1178,24 @@ class SamplesTest < ApplicationSystemTestCase test 'should be able to toggle metadata' do visit namespace_project_samples_url(@namespace, @project) assert_selector 'label', text: I18n.t(:'projects.samples.shared.metadata_toggle.label'), count: 1 - assert_selector 'table#samples-table thead tr th', count: 6 + assert_selector '#samples-table table thead tr th', count: 6 find('label', text: I18n.t('projects.samples.shared.metadata_toggle.label')).click - assert_selector 'table#samples-table thead tr th', count: 8 - within first('table#samples-table tbody tr:nth-child(3)') do + assert_selector '#samples-table table thead tr th', count: 8 + within first('#samples-table table tbody tr:nth-child(3)') do assert_text @sample3.name assert_selector 'td:nth-child(6)', text: 'value1' assert_selector 'td:nth-child(7)', text: 'value2' end find('label', text: I18n.t('projects.samples.shared.metadata_toggle.label')).click - assert_selector 'table#samples-table thead tr th', count: 6 + assert_selector '#samples-table table thead tr th', count: 6 end test 'can sort samples by metadata column' do visit namespace_project_samples_url(@namespace, @project) assert_selector 'label', text: I18n.t('projects.samples.shared.metadata_toggle.label'), count: 1 - assert_selector 'table#samples-table thead tr th', count: 6 + assert_selector '#samples-table table thead tr th', count: 6 find('label', text: I18n.t('projects.samples.shared.metadata_toggle.label')).click - assert_selector 'table#samples-table thead tr th', count: 8 + assert_selector '#samples-table table thead tr th', count: 8 within 'div.overflow-x-auto' do |div| div.scroll_to div.find('table thead th:nth-child(7)') @@ -1184,41 +1204,41 @@ class SamplesTest < ApplicationSystemTestCase click_on 'metadatafield1' assert_selector 'table thead th:nth-child(6) svg.icon-arrow_up' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample3.puid + assert_selector 'tr:first-child th', text: @sample3.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample3.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample1.puid + assert_selector 'tr:nth-child(2) th', text: @sample1.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample1.name - assert_selector 'tr:last-child td:first-child', text: @sample2.puid + assert_selector 'tr:last-child th', text: @sample2.puid assert_selector 'tr:last-child td:nth-child(2)', text: @sample2.name end click_on 'metadatafield2' assert_selector 'table thead th:nth-child(7) svg.icon-arrow_up' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample3.puid + assert_selector 'tr:first-child th', text: @sample3.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample3.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample1.puid + assert_selector 'tr:nth-child(2) th', text: @sample1.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample1.name - assert_selector 'tr:last-child td:first-child', text: @sample2.puid + assert_selector 'tr:last-child th', text: @sample2.puid assert_selector 'tr:last-child td:nth-child(2)', text: @sample2.name end # toggling metadata again causes sort to be reset find('label', text: I18n.t('projects.samples.shared.metadata_toggle.label')).click - assert_selector 'table#samples-table thead tr th', count: 6 + assert_selector '#samples-table table thead tr th', count: 6 assert_selector 'table thead th:nth-child(4) svg.icon-arrow_down' - assert_selector 'table#samples-table tbody tr', count: 3 + assert_selector '#samples-table table tbody tr', count: 3 within first('tbody') do - assert_selector 'tr:first-child td:first-child', text: @sample1.puid + assert_selector 'tr:first-child th', text: @sample1.puid assert_selector 'tr:first-child td:nth-child(2)', text: @sample1.name - assert_selector 'tr:nth-child(2) td:first-child', text: @sample2.puid + assert_selector 'tr:nth-child(2) th', text: @sample2.puid assert_selector 'tr:nth-child(2) td:nth-child(2)', text: @sample2.name - assert_selector 'tr:last-child td:first-child', text: @sample3.puid + assert_selector 'tr:last-child th', text: @sample3.puid assert_selector 'tr:last-child td:nth-child(2)', text: @sample3.name end end @@ -1261,7 +1281,7 @@ class SamplesTest < ApplicationSystemTestCase visit namespace_project_samples_url(@namespace, @project) find('label', text: I18n.t(:'projects.samples.shared.metadata_toggle.label')).click - assert_selector 'table#samples-table thead tr th', count: 8 + assert_selector '#samples-table table thead tr th', count: 8 click_link I18n.t('projects.samples.index.import_metadata_button'), match: :first @@ -1275,9 +1295,7 @@ class SamplesTest < ApplicationSystemTestCase click_on I18n.t('projects.samples.metadata.file_imports.success.ok_button') end - within '#project_samples_list' do - assert_selector 'table thead tr th', count: 9 - end + assert_selector 'table thead tr th', count: 9 end test 'should not import metadata via invalid file type' do @@ -1393,7 +1411,7 @@ class SamplesTest < ApplicationSystemTestCase visit namespace_project_samples_url(@namespace, @project) find('label', text: I18n.t('projects.samples.shared.metadata_toggle.label')).click - assert_selector 'table#samples-table thead tr th', count: 8 + assert_selector '#samples-table table thead tr th', count: 8 click_link I18n.t('projects.samples.index.import_metadata_button'), match: :first within('div[data-projects--samples--metadata--file-import-loaded-value="true"]') do @@ -1405,7 +1423,7 @@ class SamplesTest < ApplicationSystemTestCase assert_text I18n.t('projects.samples.metadata.file_imports.errors.description') click_on I18n.t('projects.samples.metadata.file_imports.errors.ok_button') end - assert_selector 'table#samples-table thead tr th', count: 9 + assert_selector '#samples-table table thead tr th', count: 9 end test 'should not import metadata with analysis values' do @@ -1876,7 +1894,7 @@ class SamplesTest < ApplicationSystemTestCase test 'should clone samples' do project2 = projects(:project2) visit namespace_project_samples_url(@namespace, @project) - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 all('input[type=checkbox]').each { |checkbox| checkbox.click unless checkbox.checked? } end @@ -1891,7 +1909,7 @@ class SamplesTest < ApplicationSystemTestCase test 'should not clone samples with session storage cleared' do project2 = projects(:project2) visit namespace_project_samples_url(@namespace, @project) - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 all('input[type=checkbox]').each { |checkbox| checkbox.click unless checkbox.checked? } end @@ -1912,7 +1930,7 @@ class SamplesTest < ApplicationSystemTestCase test 'should not clone some samples' do project25 = projects(:project25) visit namespace_project_samples_url(@namespace, @project) - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 all('input[type=checkbox]').each { |checkbox| checkbox.click unless checkbox.checked? } end @@ -1931,10 +1949,10 @@ class SamplesTest < ApplicationSystemTestCase test 'filtering samples by list of sample puids' do visit namespace_project_samples_url(@namespace, @project) - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 - assert_selector 'tr td', text: @sample1.puid - assert_selector 'tr td', text: @sample2.puid + assert_selector 'tr th', text: @sample1.puid + assert_selector 'tr th', text: @sample2.puid end click_button I18n.t(:'components.list_filter.title') within '#list-filter-dialog' do @@ -1945,7 +1963,7 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'span.label', text: @sample2.puid click_button I18n.t(:'components.list_filter.apply') end - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 2 end click_button I18n.t(:'components.list_filter.title') @@ -1954,7 +1972,7 @@ class SamplesTest < ApplicationSystemTestCase click_button I18n.t(:'components.list_filter.clear') click_button I18n.t(:'components.list_filter.apply') end - within 'table#samples-table tbody' do + within '#samples-table table tbody' do assert_selector 'tr', count: 3 end end @@ -1966,7 +1984,7 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'input[name="sample_ids[]"]:checked', count: 0 end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '3' + assert_text 'Samples: 3' assert_selector 'strong[data-selection-target="selected"]', text: '0' end find('input[name="select"]').click @@ -1974,14 +1992,14 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'input[name="sample_ids[]"]:checked', count: 3 end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '3' + assert_text 'Samples: 3' assert_selector 'strong[data-selection-target="selected"]', text: '3' end within 'tbody' do first('input[name="sample_ids[]"]').click end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '3' + assert_text 'Samples: 3' assert_selector 'strong[data-selection-target="selected"]', text: '2' end @@ -2004,7 +2022,7 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'input[name="sample_ids[]"]:checked', count: 0 end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '3' + assert_text 'Samples: 3' assert_selector 'strong[data-selection-target="selected"]', text: '0' end @@ -2021,23 +2039,44 @@ class SamplesTest < ApplicationSystemTestCase assert_selector 'input[name="sample_ids[]"]:checked', count: 1 end within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '1' + assert_text 'Samples: 1' assert_selector 'strong[data-selection-target="selected"]', text: '1' end fill_in placeholder: I18n.t(:'groups.samples.index.search.placeholder'), with: ' ' within 'tfoot' do - assert_selector 'strong[data-selection-target="total"]', text: '3' + assert_text 'Samples: 3' assert_selector 'strong[data-selection-target="selected"]', text: '0' end end + test 'action links are disabled when a project does not contain any samples' do + login_as users(:empty_doe) + + visit namespace_project_samples_url(namespace_id: groups(:empty_group).path, + project_id: projects(:empty_project).path) + + assert_no_button I18n.t(:'projects.samples.index.clone_button') + assert_no_button I18n.t(:'projects.samples.index.transfer_button') + assert_no_button I18n.t(:'projects.samples.index.create_export_button') + end + + test 'action links are disabled when a group does not contain any projects with samples' do + login_as users(:empty_doe) + + visit group_samples_url(groups(:empty_group)) + + assert_no_button I18n.t(:'projects.samples.index.clone_button') + assert_no_button I18n.t(:'projects.samples.index.transfer_button') + assert_no_button I18n.t(:'projects.samples.index.create_export_button') + end + def retrieve_puids puids = [] - within first('table#samples-table tbody') do + within first('#samples-table table tbody') do (1..3).each do |n| - puids << first("tr:nth-child(#{n}) td:first-child").text + puids << first("tr:nth-child(#{n}) th").text end end puids diff --git a/test/system/workflow_executions/submissions_test.rb b/test/system/workflow_executions/submissions_test.rb index c0df87c0ee..92b5dbc8e8 100644 --- a/test/system/workflow_executions/submissions_test.rb +++ b/test/system/workflow_executions/submissions_test.rb @@ -252,5 +252,22 @@ class SubmissionsTest < ApplicationSystemTestCase assert_no_text I18n.t(:'groups.samples.index.workflows.button_sr') end + + test 'launch pipeline button is disabled when a project does not contain any samples' do + login_as users(:empty_doe) + + visit namespace_project_samples_url(namespace_id: groups(:empty_group).path, + project_id: projects(:empty_project).path) + + assert_no_button I18n.t(:'projects.samples.index.workflows.button_sr') + end + + test 'launch pipeline button is disabled when a group does not contain any projects with samples' do + login_as users(:empty_doe) + + visit group_samples_url(groups(:empty_group)) + + assert_no_button I18n.t(:'projects.samples.index.workflows.button_sr') + end end end diff --git a/test/test_helpers/axe_helpers.rb b/test/test_helpers/axe_helpers.rb index 5ff49aaf1e..665c10a084 100644 --- a/test/test_helpers/axe_helpers.rb +++ b/test/test_helpers/axe_helpers.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true module AxeHelpers + AccessibilityError = Class.new(StandardError) + # Skip `:region` which relates to preview page structure rather than individual component. # Skip `:aria-required-children` is broken in 4.5: https://github.com/dequelabs/axe-core/issues/3758 # Skip `:link-in-text-block` which is new and seems broken. @@ -36,7 +38,7 @@ def format_accessibility_errors(violations) # rubocop:disable Metrics ) end - def assert_accessible(excludes: []) # rubocop:disable Metrics + def assert_accessible(excludes: [], max_retry_attempts: 2) # rubocop:disable Metrics excludes = Set.new(AXE_RULES_TO_SKIP) + excludes axe_exists = page.driver.evaluate_async_script <<~JS @@ -44,38 +46,48 @@ def assert_accessible(excludes: []) # rubocop:disable Metrics callback(!!window.axe) JS - results = page.driver.evaluate_async_script <<~JS - const callback = arguments[arguments.length - 1]; - #{File.read('node_modules/axe-core/axe.min.js') unless axe_exists} - // Remove cyclic references - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#examples - const getCircularReplacer = () => { - const seen = new WeakSet(); - return (key, value) => { - if (typeof value === "object" && value !== null) { - if (seen.has(value)) { - return; + retry_attempts = 0 + + begin + results = page.driver.evaluate_async_script <<~JS + const callback = arguments[arguments.length - 1]; + #{File.read('node_modules/axe-core/axe.min.js') unless axe_exists} + // Remove cyclic references + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#examples + const getCircularReplacer = () => { + const seen = new WeakSet(); + return (key, value) => { + if (typeof value === "object" && value !== null) { + if (seen.has(value)) { + return; + } + seen.add(value); } - seen.add(value); - } - return value; + return value; + }; }; - }; - const excludedRulesConfig = {}; - for (const rule of [#{excludes.map { |id| "'#{id}'" }.join(', ')}]) { - excludedRulesConfig[rule] = { enabled: false }; - } - const options = { - elementRef: true, - resultTypes: ['violations'], - rules: { - ...excludedRulesConfig + const excludedRulesConfig = {}; + for (const rule of [#{excludes.map { |id| "'#{id}'" }.join(', ')}]) { + excludedRulesConfig[rule] = { enabled: false }; } - } - axe.run(document.body, options).then(res => JSON.parse(JSON.stringify(res, getCircularReplacer()))).then(callback); - JS + const options = { + elementRef: true, + resultTypes: ['violations'], + rules: { + ...excludedRulesConfig + } + } + axe.run(document.body, options).then(res => JSON.parse(JSON.stringify(res, getCircularReplacer()))).then(callback); + JS + + violations = results['violations'] - violations = results['violations'] + raise AccessibilityError, 'Not accessible' unless violations.empty? + rescue AccessibilityError + retry_attempts += 1 + page.driver.wait_for_network_idle + retry if retry_attempts < max_retry_attempts + end message = format_accessibility_errors(violations)