diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b5820b95a0..5decbc7a8e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,11 +11,10 @@ updates: prefix: "" labels: - "dependencies" - ignore: - - dependency-name: sprockets - versions: - - ">= 4.a" - - "< 5" + groups: + rubocop: + patterns: + - "rubocop*" - package-ecosystem: npm directory: "/" schedule: @@ -27,11 +26,17 @@ updates: prefix: "" labels: - "dependencies" - ignore: - - dependency-name: webpack-cli - versions: - - ">= 4.a" - - "< 5" + groups: + babel: + patterns: + - "@babel*" + - "babel*" + eslint: + patterns: + - "eslint*" + rails: + patterns: + - "@rails*" - package-ecosystem: github-actions directory: "/" schedule: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0945f0cf9d..eb2ebb39d3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f46df8aef4..b06e3ac766 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: name: Lint Ruby runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use ruby from .ruby-version uses: ruby/setup-ruby@v1 with: @@ -29,7 +29,7 @@ jobs: name: Lint JavaScript runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Node uses: actions/setup-node@v3 with: @@ -49,7 +49,7 @@ jobs: name: Lint CSS runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Node uses: actions/setup-node@v3 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7543416ab9..ab3bbcd1e8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: - 3306:3306 options: --health-cmd="healthcheck.sh --su-mysql --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use ruby from .ruby-version uses: ruby/setup-ruby@v1 with: @@ -64,7 +64,7 @@ jobs: name: Test JavaScript runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Node uses: actions/setup-node@v3 with: @@ -102,7 +102,7 @@ jobs: - 3306:3306 options: --health-cmd="healthcheck.sh --su-mysql --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use ruby from .ruby-version uses: ruby/setup-ruby@v1 with: diff --git a/.rubocop.yml b/.rubocop.yml index 27da4e1189..af09f01fd3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,8 @@ -require: rubocop-rails +require: + - rubocop-rails + - rubocop-factory_bot + - rubocop-capybara + - rubocop-minitest AllCops: Exclude: @@ -83,3 +87,9 @@ Style/OptionalBooleanParameter: Enabled: false Style/HashSyntax: EnforcedShorthandSyntax: never + +FactoryBot/ConsistentParenthesesStyle: + EnforcedStyle: omit_parentheses + +Minitest/MultipleAssertions: + Enabled: false diff --git a/Gemfile b/Gemfile index 3c1c64c6e9..e2a500933d 100644 --- a/Gemfile +++ b/Gemfile @@ -4,11 +4,11 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '~> 3.1.2' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '~> 7.0.7' +gem 'rails', '~> 7.0.8' # Use mysql as the database for Active Record gem 'mysql2', '~> 0.5.5' # Use Puma as the app server -gem 'puma', '~> 6.3.0' +gem 'puma', '~> 6.3.1' # Use dart-sass for stylesheets gem 'cssbundling-rails', '~> 1.2.0' @@ -63,7 +63,7 @@ gem 'diff-lcs', '~>1.5' gem 'ace-rails-ap', '~>4.5' # auto css prefixer -gem 'autoprefixer-rails', '~>10.4.13' +gem 'autoprefixer-rails', '~>10.4.15' # saml authentication gem 'devise', '~>4.9.2' @@ -106,7 +106,7 @@ gem 'ed25519' # i18n gem 'i18n-js', '~> 4.2.3' -gem 'rails-i18n', '~> 7.0.7' +gem 'rails-i18n', '~> 7.0.8' # email exceptions gem 'exception_notification', '~> 4.5.0' @@ -135,13 +135,13 @@ gem 'memory_profiler', '~> 1.0.1' gem 'rack-mini-profiler', '~> 3.1.1' gem 'stackprof', '~> 0.2.25' -gem 'ddtrace', '~> 1.13.0' +gem 'ddtrace', '~> 1.14.0' # Make sure filesystem changes only happen at the end of a transaction gem 'after_commit_everywhere', '~> 1.3.1' # More advanced counter_cache that allows conditions -gem 'counter_culture', '~> 3.4' +gem 'counter_culture', '~> 3.5' group :development, :test do # Use mocha for stubbing and mocking @@ -157,7 +157,7 @@ group :development, :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '~> 3.39.2' - gem 'selenium-webdriver', '~> 4.11.0' + gem 'selenium-webdriver', '~> 4.12.0' end group :test do @@ -165,7 +165,7 @@ group :test do gem 'minitest-ci', '~> 3.4.0' gem 'simplecov', '~> 0.22.0', require: false gem 'simplecov-cobertura', '~> 2.1.0', require: false - gem 'test-prof', '~> 1.2.2' + gem 'test-prof', '~> 1.2.3' # Mocking HTTP requests to third parties. gem 'webmock' @@ -177,9 +177,12 @@ end group :development do # Access an IRB console on exception pages or by using <%= console %> in views gem 'listen', '~> 3.8.0' - gem 'web-console', '~> 4.2.0' + gem 'web-console', '~> 4.2.1' gem 'rb-readline', '~> 0.5.5' # require for irb + gem 'rubocop-capybara', '~> 2.18.0' + gem 'rubocop-factory_bot', '~> 2.23' + gem 'rubocop-minitest', '~> 0.31.1' gem 'rubocop-rails', '~> 2.20.2' # for opening letters diff --git a/Gemfile.lock b/Gemfile.lock index e708789ec1..19096836cb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,72 +2,72 @@ GEM remote: https://rubygems.org/ specs: ace-rails-ap (4.5) - actioncable (7.0.7) - actionpack (= 7.0.7) - activesupport (= 7.0.7) + actioncable (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.7) - actionpack (= 7.0.7) - activejob (= 7.0.7) - activerecord (= 7.0.7) - activestorage (= 7.0.7) - activesupport (= 7.0.7) + actionmailbox (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.7) - actionpack (= 7.0.7) - actionview (= 7.0.7) - activejob (= 7.0.7) - activesupport (= 7.0.7) + actionmailer (7.0.8) + actionpack (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activesupport (= 7.0.8) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.7) - actionview (= 7.0.7) - activesupport (= 7.0.7) + actionpack (7.0.8) + actionview (= 7.0.8) + activesupport (= 7.0.8) rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.7) - actionpack (= 7.0.7) - activerecord (= 7.0.7) - activestorage (= 7.0.7) - activesupport (= 7.0.7) + actiontext (7.0.8) + actionpack (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.7) - activesupport (= 7.0.7) + actionview (7.0.8) + activesupport (= 7.0.8) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.7) - activesupport (= 7.0.7) + activejob (7.0.8) + activesupport (= 7.0.8) globalid (>= 0.3.6) - activemodel (7.0.7) - activesupport (= 7.0.7) - activerecord (7.0.7) - activemodel (= 7.0.7) - activesupport (= 7.0.7) - activestorage (7.0.7) - actionpack (= 7.0.7) - activejob (= 7.0.7) - activerecord (= 7.0.7) - activesupport (= 7.0.7) + activemodel (7.0.8) + activesupport (= 7.0.8) + activerecord (7.0.8) + activemodel (= 7.0.8) + activesupport (= 7.0.8) + activestorage (7.0.8) + actionpack (= 7.0.8) + activejob (= 7.0.8) + activerecord (= 7.0.8) + activesupport (= 7.0.8) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.7) + activesupport (7.0.8) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.4) + addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) aes_key_wrap (1.1.0) after_commit_everywhere (1.3.1) @@ -80,7 +80,7 @@ GEM rake (>= 10.4, < 14.0) ast (2.4.2) attr_required (1.0.1) - autoprefixer-rails (10.4.13.0) + autoprefixer-rails (10.4.15.0) execjs (~> 2) bcrypt (3.1.18) bcrypt_pbkdf (1.1.0) @@ -120,7 +120,7 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) concurrent-ruby (1.2.2) - counter_culture (3.4.0) + counter_culture (3.5.0) activerecord (>= 4.2) activesupport (>= 4.2) crack (0.4.5) @@ -133,7 +133,7 @@ GEM daemons (1.4.1) dalli (3.2.5) date (3.3.3) - ddtrace (1.13.0) + ddtrace (1.14.0) debase-ruby_core_source (= 3.2.1) libdatadog (~> 3.0.0.1.0) libddwaf (~> 1.9.0.0.0) @@ -183,8 +183,8 @@ GEM ffi (1.15.5) flamegraph (0.9.5) glob (0.4.0) - globalid (1.1.0) - activesupport (>= 5.0) + globalid (1.2.1) + activesupport (>= 6.1) hana (1.3.7) has_scope (0.8.1) actionpack (>= 5.2) @@ -257,7 +257,7 @@ GEM method_source (1.0.0) mini_magick (4.11.0) mini_mime (1.1.5) - minitest (5.19.0) + minitest (5.20.0) minitest-ci (3.4.0) minitest (>= 5.0.6) minitest-utils (0.4.8) @@ -338,8 +338,8 @@ GEM premailer (~> 1.7, >= 1.7.9) pretender (0.5.0) actionpack (>= 6.1) - public_suffix (5.0.1) - puma (6.3.0) + public_suffix (5.0.3) + puma (6.3.1) nio4r (~> 2.0) pundit (2.3.1) activesupport (>= 3.0.0) @@ -358,20 +358,20 @@ GEM rack rack-test (2.1.0) rack (>= 1.3) - rails (7.0.7) - actioncable (= 7.0.7) - actionmailbox (= 7.0.7) - actionmailer (= 7.0.7) - actionpack (= 7.0.7) - actiontext (= 7.0.7) - actionview (= 7.0.7) - activejob (= 7.0.7) - activemodel (= 7.0.7) - activerecord (= 7.0.7) - activestorage (= 7.0.7) - activesupport (= 7.0.7) + rails (7.0.8) + actioncable (= 7.0.8) + actionmailbox (= 7.0.8) + actionmailer (= 7.0.8) + actionpack (= 7.0.8) + actiontext (= 7.0.8) + actionview (= 7.0.8) + activejob (= 7.0.8) + activemodel (= 7.0.8) + activerecord (= 7.0.8) + activestorage (= 7.0.8) + activesupport (= 7.0.8) bundler (>= 1.15.0) - railties (= 7.0.7) + railties (= 7.0.8) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -383,12 +383,12 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - rails-i18n (7.0.7) + rails-i18n (7.0.8) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) - railties (7.0.7) - actionpack (= 7.0.7) - activesupport (= 7.0.7) + railties (7.0.8) + actionpack (= 7.0.8) + activesupport (= 7.0.8) method_source rake (>= 12.2) thor (~> 1.0) @@ -418,6 +418,12 @@ GEM unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.29.0) parser (>= 3.2.1.0) + rubocop-capybara (2.18.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.23.1) + rubocop (~> 1.33) + rubocop-minitest (0.31.1) + rubocop (>= 1.39, < 2.0) rubocop-rails (2.20.2) activesupport (>= 4.2.0) rack (>= 1.1) @@ -430,7 +436,7 @@ GEM ffi (~> 1.12) ruby2_keywords (0.0.5) rubyzip (2.3.2) - selenium-webdriver (4.11.0) + selenium-webdriver (4.12.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) @@ -470,9 +476,9 @@ GEM attr_required (>= 0.0.5) faraday (~> 2.0) faraday-follow_redirects - terser (1.1.17) + terser (1.1.18) execjs (>= 0.3.0, < 3) - test-prof (1.2.2) + test-prof (1.2.3) thor (1.2.2) tilt (2.0.11) timeout (0.4.0) @@ -491,7 +497,7 @@ GEM version_gem (1.1.0) warden (1.2.9) rack (>= 2.0.9) - web-console (4.2.0) + web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) @@ -500,7 +506,7 @@ GEM activesupport faraday (~> 2.0) faraday-follow_redirects - webmock (3.18.1) + webmock (3.19.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -520,7 +526,7 @@ DEPENDENCIES ace-rails-ap (~> 4.5) after_commit_everywhere (~> 1.3.1) annotate (~> 3.2.0) - autoprefixer-rails (~> 10.4.13) + autoprefixer-rails (~> 10.4.15) bcrypt_pbkdf bootsnap (~> 1.16.0) builder (~> 3.2.4) @@ -531,10 +537,10 @@ DEPENDENCIES capistrano-yarn (~> 2.0.2) capistrano3-delayed-job (~> 1.7.6) capybara (~> 3.39.2) - counter_culture (~> 3.4) + counter_culture (~> 3.5) cssbundling-rails (~> 1.2.0) dalli (~> 3.2.5) - ddtrace (~> 1.13.0) + ddtrace (~> 1.14.0) delayed_job_active_record (~> 4.1.7) delayed_job_web (~> 1.4.4) devise (~> 4.9.2) @@ -573,27 +579,30 @@ DEPENDENCIES omniauth_openid_connect (~> 0.7.1) premailer-rails (~> 1.12.0) pretender (~> 0.5.0) - puma (~> 6.3.0) + puma (~> 6.3.1) pundit (~> 2.3.1) rack-mini-profiler (~> 3.1.1) - rails (~> 7.0.7) + rails (~> 7.0.8) rails-controller-testing (~> 1.0.5) - rails-i18n (~> 7.0.7) + rails-i18n (~> 7.0.8) rb-readline (~> 0.5.5) rouge (= 4.1.3) + rubocop-capybara (~> 2.18.0) + rubocop-factory_bot (~> 2.23) + rubocop-minitest (~> 0.31.1) rubocop-rails (~> 2.20.2) ruby-saml (~> 1.15.0) rubyzip (~> 2.3.2) - selenium-webdriver (~> 4.11.0) + selenium-webdriver (~> 4.12.0) simplecov (~> 0.22.0) simplecov-cobertura (~> 2.1.0) slack-notifier (~> 2.4.0) sprockets-rails (~> 3.4.2) stackprof (~> 0.2.25) terser (>= 1.1.1) - test-prof (~> 1.2.2) + test-prof (~> 1.2.3) tzinfo-data - web-console (~> 4.2.0) + web-console (~> 4.2.1) webmock will_paginate (~> 4.0.0) diff --git a/app/assets/javascripts/components/annotations/annotation_form.ts b/app/assets/javascripts/components/annotations/annotation_form.ts index cbf412d866..2643261faa 100644 --- a/app/assets/javascripts/components/annotations/annotation_form.ts +++ b/app/assets/javascripts/components/annotations/annotation_form.ts @@ -50,6 +50,8 @@ export class AnnotationForm extends watchMixin(ShadowlessLitElement) { @property({ state: true }) _savedAnnotationTitle: string; @property({ state: true }) + _savedAnnotationSearchInput = ""; + @property({ state: true }) saveAnnotation = false; get savedAnnotationTitle(): string { @@ -69,6 +71,15 @@ export class AnnotationForm extends watchMixin(ShadowlessLitElement) { }, savedAnnotationId: () => { this._savedAnnotationId = this.savedAnnotationId || ""; + }, + saveAnnotation: () => { + this.listenForCloseIfEmpty(); + }, + _annotationText: () => { + this.listenForCloseIfEmpty(); + }, + _savedAnnotationSearchInput: () => { + this.listenForCloseIfEmpty(); } }; @@ -108,13 +119,23 @@ export class AnnotationForm extends watchMixin(ShadowlessLitElement) { } } - connectedCallback(): void { - super.connectedCallback(); - if (!this.annotationText) { + get isEmpty(): boolean { + return this._annotationText.length === 0 && !this.saveAnnotation && this._savedAnnotationSearchInput.length === 0; + } + + listenForCloseIfEmpty(): void { + if (this.isEmpty) { this.listenForClose(); + } else { + this.stopListeningForClose(); } } + connectedCallback(): void { + super.connectedCallback(); + this.listenForCloseIfEmpty(); + } + disconnectedCallback(): void { super.disconnectedCallback(); this.stopListeningForClose(); @@ -125,15 +146,11 @@ export class AnnotationForm extends watchMixin(ShadowlessLitElement) { this._annotationText = e.detail.text; } this._savedAnnotationId = e.detail.id; + this._savedAnnotationSearchInput = e.detail.title; } handleTextInput(): void { this._annotationText = this.inputRef.value.value; - if (this._annotationText.length > 0) { - this.stopListeningForClose(); - } else { - this.listenForClose(); - } } handleCancel(): void { diff --git a/app/assets/javascripts/components/annotations/create_annotation_button.ts b/app/assets/javascripts/components/annotations/create_annotation_button.ts index 6b3f87011f..1c2e570f2c 100644 --- a/app/assets/javascripts/components/annotations/create_annotation_button.ts +++ b/app/assets/javascripts/components/annotations/create_annotation_button.ts @@ -118,11 +118,11 @@ export class CreateAnnotationButton extends ShadowlessLitElement { @pointerover=${() => userAnnotationState.isCreateButtonExpanded = true} @pointerout=${() => userAnnotationState.isCreateButtonExpanded = false} > - + `; } diff --git a/app/assets/javascripts/components/annotations/line_of_code.ts b/app/assets/javascripts/components/annotations/line_of_code.ts index e8942e82e7..ceb4bdbb96 100644 --- a/app/assets/javascripts/components/annotations/line_of_code.ts +++ b/app/assets/javascripts/components/annotations/line_of_code.ts @@ -61,6 +61,10 @@ export class LineOfCode extends ShadowlessLitElement { .sort(compareAnnotationOrders); } + hasValidColumn(annotation: Annotation): boolean { + return annotation.column !== undefined && annotation.column !== null && annotation.column >= 0 && annotation.column < this.codeLength; + } + /** * Calculates the range of the code that is covered by the given annotation. * If the annotation spans multiple lines, the range will be the whole line unless this is the first or last line. @@ -81,12 +85,17 @@ export class LineOfCode extends ShadowlessLitElement { let start = 0; if (this.row === firstRow) { - start = annotation.column || 0; + // In rare cases, machine annotations start past the end of the line, resulting in errors if we don't constrain them to be in the line + if (this.hasValidColumn(annotation)) { + start = annotation.column; + } else { + start = 0; + } } let length = this.codeLength - start; if (this.row === lastRow) { - if (annotation.column !== undefined && annotation.column !== null) { + if (this.hasValidColumn(annotation)) { const defaultLength = isMachineAnnotation ? 0 : this.codeLength - start; length = annotation.columns || defaultLength; } diff --git a/app/assets/javascripts/saved_annotation_beta.ts b/app/assets/javascripts/saved_annotation_beta.ts index 2d5a94469c..769bcd0ca2 100644 --- a/app/assets/javascripts/saved_annotation_beta.ts +++ b/app/assets/javascripts/saved_annotation_beta.ts @@ -2,7 +2,7 @@ import { courseState } from "state/Courses"; -const BETA_COURSES = new Set([10, 773, 1151, 1659, 2258, 2263]); +const BETA_COURSES = new Set([10, 773, 1151, 1659, 1662, 2258, 2263]); export function isBetaCourse(courseId?: number): boolean { return BETA_COURSES.has(courseId || courseState.id ); diff --git a/app/assets/stylesheets/components/code_listing.css.scss b/app/assets/stylesheets/components/code_listing.css.scss index 21bb1b5cee..f7b3b27d00 100644 --- a/app/assets/stylesheets/components/code_listing.css.scss +++ b/app/assets/stylesheets/components/code_listing.css.scss @@ -203,7 +203,7 @@ pre { padding-left: 5px; - white-space: pre-wrap; + white-space: break-spaces; word-break: break-word; min-height: 20px; } diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7d0babbe3f..89f54ca91f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -144,8 +144,12 @@ def markdown_unsafe(source) end def sanitize(html) - @tags ||= Rails::Html::SafeListSanitizer.allowed_tags.to_a + %w[table thead tbody tr td th colgroup col style svg circle line rect path summary details] - @attributes ||= Rails::Html::SafeListSanitizer.allowed_attributes.to_a + %w[style target data-bs-toggle data-parent data-tab data-line data-element id x1 y1 x2 y2 stroke stroke-width fill cx cy r] + @tags ||= Rails::Html::SafeListSanitizer.allowed_tags.to_a + + %w[table thead tbody tr td th colgroup col style summary details] + + %w[svg g style circle line rect path polygon text] + @attributes ||= Rails::Html::SafeListSanitizer.allowed_attributes.to_a + + %w[style target data-bs-toggle data-parent data-tab data-line data-element id] + + %w[viewbox width height version style class transform id x y rx ry x1 y1 x2 y2 d points fill stroke stroke-width cx cy r font-size font-family font-weight font-variant] # Filters allowed tags and attributes sanitized = ActionController::Base.helpers.sanitize html, diff --git a/app/policies/saved_annotation_policy.rb b/app/policies/saved_annotation_policy.rb index c146d37258..973245b271 100644 --- a/app/policies/saved_annotation_policy.rb +++ b/app/policies/saved_annotation_policy.rb @@ -1,6 +1,6 @@ class SavedAnnotationPolicy < ApplicationPolicy # REMOVE AFTER CLOSED BETA - BETA_COURSES = [10, 773, 1151, 1659, 2258, 2263].freeze + BETA_COURSES = [10, 773, 1151, 1659, 1662, 2258, 2263].freeze class Scope < ApplicationPolicy::Scope def resolve diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index e2907c74aa..e87c55720e 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -NKut7ZC0yxFzlD4BROG45cYiVN8MOeiAkyxsFMf3NCA6nx/DsYmVkukzU/6TWT7kMC6h2rS7iAcabFqSNztlbmF1qM58RYmrk6ZCYD3Q/Q9KlQAP/cjqIr+EnGsuhur5VohmN3Hq34LO0xOQpAxGj+9W/p6AqO2RlrsZ456XoSn/9iolTeM0XWS8wu1CqeD+4AbBQdIimq0HO7YmwlkfenEhZN3SC2BgbUA5PlJNaSZcpBsQ7X5hjI2/Q1veeBvrwT34qisefqbPok1khBEpYicgoYQBuh3scyY8ZPE8LdKSCF5fibJomZw2A39iOQWFeqcUvujqP9nM5QqyLfnrpE0JQlIj0mO4NDEclFFHBMUUqSWJh2qqVySyk0x8SsNVD5IFkgvlqGCBNNdGPHNVzYL7WfTjqHKna01yI2+9IKY3vjas8dNLEICPPjm4sqPAPSL7CuKwjFiMZD1HF1h6da9A+W8uiiAyJ2ry+4XiolGezS8FPBhp3qeDhgK5szfpMqePzWWL41oYVcPb1MZl8QCKOR1rfhphwRooVPfV0ziV+/QVTkNHsoTxDutWtbPvWhAO7qVQx3/FirLh6hmiBLv0sAAkQT2Dq/WbNxMUc97mkTewcRGs4D1Q6Ul+s07YTgspS4YCfSLtjr7vmRMOn3D+WjzxCpVZ9F+SyrFHWaqjG/t0GjQstJ5s4WhligDsK4i/uFGNWOa2duh+KytFjHVIS0OFO3LfrTyaPj0S7jdWyEuAmg9xlqzDqjuFrKtlGe0lyHdzvAXnxUOkb8N183ywRCFDD5Rc56+VRPsR5sepReRAM+zJNPy8hk0iQ2vfFGVvo/GifsyCs0yNj8wHSqxkfxO8my2mmyAelgscJO+LoBGF33rVTrtL4zM2nfqAU30t8UthHIMyrnsshdJ1GQFMFxJyxPnXJJV+7j3pB5kphsuEkecqbjUFxlhU4u/WLh3b0WMCGNjTV25MdVjWWlboaQv+1bbemxEf71H7VQciUiuz223l4YiNd4qaE1XQR6GwZvf3kD/nhbEuFDKMlioRiudkGmoBjZZ2msDSbncF2Gs9btAf0ujxx4Z6ATaGBe+z+MEMdJOZM9WJtM/FQh23Bik/J7bvjNTxfYKmWEAzSf3lTb7UB3BFJ2X6DXtOcLVFWJBFdtZ88Tko7A8mLkY7+pugGzWiL+BQyHPxkhCKgUWFZ1vC2WlB1lZFWp2QPKXEy/XuUWspzKpvZdtT+OC1BDIU3vylLtbBP0MFSbdubbis83xbV/p/1/JASwUdoJrKqQtwHWsx/NL9Et4pBWT0pZFjZhMhHXORRh9eYT6m53dO2gILzPB3ApM2b0Uv3XQhyzpHRgI5tYfIx6IiCkyAef5yb+Vcc4mfw3DmiPAJf8BjasUgPbfMtdZ2b3eCi8zCEoCNkacalvg9xLyxKok+L/vH0wnGJ3//WbQRrgyg/arQz5Lhnoxyk9uz--scusuPTiG1W0cusQ--YVMze8lIpH0+tkBywZ676A== \ No newline at end of file +HK5nEb7Obp66Y79JPxk8eDcvdMkiKxX49gZstAf3db6Da6bpOeuEr9biEKow07kOuhd/OqWdODtrk9Iz2Clilk2HbpaEqGXQxPPISnG3UY+r/vuLzkULbyPJlPxDVq0MsaSifVDqZAtkNhaO/p28YtNNl04+tDtYs/No00HvkEhbFwUOuPX6DQkOB5PG547v2rUVvLZ0K6O5PuNS+8VA4N21cgGrLtVTLPsjY0CghJ/A4fqLGkEypJuEQyaVUlEkaMb36lWTUZap4Xr6DHXOusGxX0698UlThK4GKmEhxZR0UsMtp5djZa6/v0YR1O5cjzl82Pe/3tOgrN3BupfSa7yeIuhH3DoHPwNHo3JrwyypW2FMcuhxjO8VCfAyYEBky/TJLqUdJnbx7++zjKTedC0UNLIxU9rKSsGsjDm+zK5UcF7dbpLFVBtH/ZdfeWnaZL/CiZPcBmOrtrVGMQkWp0921kLPbLWzxURA1H4cVJ3Zj/wOzqJhb253kFH9mTkN8+r8uKjcH4mp/bBk0R0USBmqy6ewEgb8NTHKPBJb9smHZywA2bVQGjFm3fUL0Tb0YCcZqs+CdRgaXzc/ShMj758YiXK6o5n6yrAlumbLoS1wMCaxzgbLmFcD8s3FeCI79fcpoiTkUQLzPAi7seUh5euU/W665vKe6Sc4LzRP5Tmh8Y2dz2TnQmCWYr9Z2QfUZlMRYCdUfILPinv1XkmfgZ+dSljTupaBLdSmcV/ssQbgGXUitEDHlN3ZYlIXALDckmL701E1/jM9xoMQg7QQ90NYQjcmYSuiORTBapLoy5lUMjSBXv8p8JXQ/zR3R26P9C9ODYMYJzePJB6+NC01zT2qfET6/XpU0M89kGxmahdopu/27YCe6DZFTPnIrQ7lFgWOdM8CJF9wdaVozfg0P8Ck/tS0EjZiQ1U6oC7NXBk67zr9+OZZixTOTUKBHO0tzmeX4/11kOAwg1FUAMeaP/CztdrDIMa/1olUMTPAzI4OY3+I+WoOOotOvv64lie/JJqm9LRQvjbGIQY6/T21wKR8K//rkKN53ODogAjBHEbLNFTjXTVkxKuXIkpJ+aSIxd9+CTANb44DKeLXB0FLz+fmMRoiZFbFMW1c45CaPAlROxiklqvOqyget/oAjbm6XCHEi4k9fruLqwWMsRgLkG2tUArBusybqsdKi6K7o0Esm0JrLVy1ACh2APadGhWMgtY2Wqwo+IQeZEdUCm2rLRYkuaXEKeNrSK2dJYFYoCEtzmiuTqIpocikMhLhVp/FW6xCxn+1PNqNIfw2fq52Zb/cEfIllDT9ZCEXTdAsPjy8/MY0jBAh/E1WvCBXaZSSfi3xLv79fEVq/eBKV7mmB7eVGCXT9gDn+A0mIuNkGlXjVaJCBbKnwByK0BF5Q1LhwJI8Nl+Y0FLiSmmW/Uf7GUnPcUoVeIBAiqj0LvYk3dmcjKWSqRGPwnjPa1qz--SMu6mv2AvOVU+3ZM--W7kqAiucIEpkAl4PMCk1sw== \ No newline at end of file diff --git a/config/credentials/staging.yml.enc b/config/credentials/staging.yml.enc index c384e1da86..788b946c2d 100644 --- a/config/credentials/staging.yml.enc +++ b/config/credentials/staging.yml.enc @@ -1 +1 @@ -al0q9opCHbAVHHHLTcCX9xDyaLVS1wURHrSkhFXnhiUvuYFOwEJ46CaSpv/hB0+R2COj4XWpoo/oaBCXoEIz8awtcu23Vkio62XpJdnHIE88BrWU2ft/k5oDxojL8GVlBlL4ancOgChrKxSa9CWza25nr560x4nnAgBBRrA1QHkyNlXHe8t//zo3+wOK/FBeS4eR4qMYeb5eNjfmbLTecjjxaH9scKUDGwXutgCyOJc4rpIGLqXelzcmUpSrmVr+w4K0naQ7VZMr1C0JTOPWJj1agOGrVRSLTsg1ArAYzew+sbLr1HTOzFcuI7VFJx0SguUnVd/reizylhoURytzazdNvZlY53mJC5qwZuLuQQfGCzmfC5P9BV1Ai3kiJyZpPGrUJWZiBw69sOss+oEI1cIU7Pers8rGKSA+t845ZyfqQBo7LCkcbjo2GoKi6Vce+SVET9aPfVgXp7yMwMzgr0xRaP6gkHzJLU65mNJS+nc+VBd9RdTK2mV4TaYzlYFUSXC3ceJHrFK4EaviGBEyhh7w72mcAolwjjedSTJgwe3JT4MVrNIWcmI8hPHlPJ6eWKtqzr3/eMuYZPjD9O9emRmBeM9l9+6RpyCrzQe9ERzQ66+nr66WY8lwwfhe5uhDWllFc91xwlhejLQppRrfk1kjtOKnJpehDDEVkhn6EGbQxn033VXkao91BNRqzm30uIviCR3J9i4tfgBf7A1v8+aRxN6SBmq+S4wB+niAEUe0gQQi1NzfZdIj7jjCBGMfnkie2azd7dpSdTghpQE4pgd9rGhq6GqfQYOM1mebauUmzlIVOmOJycGDM77CAMGcXyOOBBNFAcwwjHgIM8RZmdet3xiofiwcEMZWkC4PWUmtN+MyqpIlOiavsBLEbRF2BO3vePxzkMaMUhIDhIHwDQtTcVg9s82iRnMXIp0CG7IKv96w0VDMBYI9dZwzAhQ6e02LB2WLjzcxLY4cE2Uj4Sbe2Rzml0W86PIsXTeGnux+5Vg39b9p9OUGO5HSNxaCdE6p9Nk3a3mhNv2BY4oids2NIBvnXhsSpbSgcD3np8CgEfyOD0O7hl3lrxLf2DfcYCiiuA7jp2xq//h0iYKmphSw1Yub1mWZ--iqcKxvxt2LNltDKA--aS+jsZxGyhRDq6u+Se7c/A== \ No newline at end of file +FtEacz67m78JlWej2mS79IyO8jDCtCb0q5VAgrfzcRMwUrhd/npLcaxUC5lju07PRmvS23o2yR4iv6twgZ++YuU52j3VUkrBA+tpjO/JQjZIxYmF3YH88OOgludhGJDsKO3Piz12Ha6PFup6dcX0sOJInJeA8OFnJgGB2L+j4xULDidBfVvN9qBRECmHIcx4DrCPBNa30CbtPWpnM8fxtCQdqI9w0gZdQTTLvf8AYD7fQHsJjZ/JQdkJlyixHlFK2nCwJht9zj6a4VK+JwXzNueKR1XolVijXgPo/vx9RK3ZFfvieVhoRx8A/yZtmHxdDMLRoWWdjq5ruudUSM91z+7a6IZTncuf4bx/Q9gGMdFI9P1Ly9JnfhqJ+6yeeqlbo9As3wrjPIdGD54OSJE/vpvmle9cbop2GU7pq/9YwQC9yKoJizPOGqdDsqZUXgU99mKP9UjFDs2e1w849rJn3IyPcYlWN4s/ha/Z4nQAMkXoU4SzGyImNZv1Ne2oLPVEUCHvPppsal/lS4EjOFoKOVQ9n98DUu2VryFUUW7EoBZdobdHlhE7+sMd7j7Knrd8o3kEiddK9aVPBny9wOzu1D1Bv+zM3dKE+tLUIbJxN/Y9upGwdDo3Lo2BIIYE/gyGIc4i5W4nG8aXqhRFrmdKhhY2iQRM5RXY1UGOCwjX9AZTGzm8k0ru4me6r8KbOAOs7DxrE1SzzjQuc036yEsCPb8bT2G5uWFf80D4ZEUaEQmBgNeoZ6GtSJt5Oi8wfl9ZpDD1gYHaXSDaDAXlH/8N1A2SqW2sOgBaGCvNdZQnOtfsvnwDblc/Cufz4oYp0IPgJL9usAVCsSRaJb+FE0E6lGBdTALcFQ4TRd3DGhHOOcPXRD4nvkuxcDQdijHYkPBz5mhUYZQqSURNopZPMA1dRh1Q6rXVDHdJTE+FeN9N7mUwJi7ALeFlyREODirSUW/Kls9oZKPTEDPDxIJ9fkbqtqXY+wKkXIJVmOsVERlh055HeYWMXB+nD3kyaqK41qcdVKb7uH7gtLDdxKL2oVZMEQb1vD/jN8AKv9kaSlnR3mV0SWEZVls8La+ZMlh7MI7DHcHgIc06gBO4yDGgzh6LDeaBwa5dMvvK--gylYIlQZnfRkadmD--50UxMH/J6luJOiKmTdDNfQ== \ No newline at end of file diff --git a/package.json b/package.json index 3325788604..fb0321da9a 100644 --- a/package.json +++ b/package.json @@ -12,23 +12,23 @@ "test:system:coverage:merge": "nyc merge ./coverage/system-js ./coverage/coverage.json" }, "dependencies": { - "@babel/core": "^7.22.10", - "@babel/plugin-proposal-decorators": "^7.22.10", + "@babel/core": "^7.22.17", + "@babel/plugin-proposal-decorators": "^7.22.15", "@babel/plugin-proposal-object-rest-spread": "^7.20.7", "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-runtime": "^7.22.10", - "@babel/preset-env": "^7.22.10", - "@babel/preset-typescript": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.22.15", + "@babel/preset-env": "^7.22.15", + "@babel/preset-typescript": "^7.22.15", "@dodona/papyros": "^1.0.1", "@popperjs/core": "^2.11.8", - "@rails/activestorage": "^7.0.7", - "@rails/ujs": "^7.0.7", + "@rails/activestorage": "^7.0.8", + "@rails/ujs": "^7.0.8", "@types/d3": "^7.4.0", "babel-loader": "^9.1.3", "babel-plugin-macros": "^3.1.0", "bootstrap": "5.3.1", "clipboard": "^2.0.11", - "core-js": "^3.32.0", + "core-js": "^3.32.2", "d3": "^7.8.5", "dayjs": "^1.11.9", "dragula": "^3.7.3", @@ -38,7 +38,7 @@ "iframe-resizer": "^4.3.6", "lit": "2.8.0", "node-polyglot": "^2.5.0", - "sass": "^1.65.1", + "sass": "^1.66.1", "tippy.js": "^6.3.7", "typescript": "^4.9.5", "webpack": "^5.88.2", @@ -53,11 +53,11 @@ "@types/dragula": "^3.7.1", "@types/iframe-resizer": "^3.5.9", "@types/jest": "^27.5.0", - "@types/serviceworker": "^0.0.72", + "@types/serviceworker": "^0.0.73", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "babel-plugin-istanbul": "^6.1.1", - "eslint": "^8.47.0", + "eslint": "^8.49.0", "eslint-config-google": "^0.14.0", "eslint-plugin-jest": "^27.2.3", "eslint-plugin-lit": "^1.9.1", @@ -66,10 +66,10 @@ "jest": "^26.6.3", "jest-junit": "^16.0.0", "nyc": "^15.1.0", - "stylelint": "^15.10.2", + "stylelint": "^15.10.3", "stylelint-config-standard": "^34.0.0", - "stylelint-config-standard-scss": "^10.0.0", + "stylelint-config-standard-scss": "^11.0.0", "ts-jest": "^26.5.6", - "webpack-bundle-analyzer": "^4.9.0" + "webpack-bundle-analyzer": "^4.9.1" } } diff --git a/test/controllers/activities_controller_test.rb b/test/controllers/activities_controller_test.rb index 9ee165a9fd..70a0e50ece 100644 --- a/test/controllers/activities_controller_test.rb +++ b/test/controllers/activities_controller_test.rb @@ -11,28 +11,33 @@ def setup test 'should show activity' do get activity_url(@instance) + assert_response :success end test 'should show activity if removed' do @instance.update(status: :removed, path: nil) get activity_url(@instance) + assert_response :success end test 'should show activity description' do get description_activity_url(@instance, token: @instance.access_token) + assert_response :success end test 'should show content_page' do cp = create :content_page get activity_url(cp) + assert_response :success end test 'should not show activity description with incorrect token' do get description_activity_url(@instance, token: 'blargh') + assert_response :forbidden end @@ -41,6 +46,7 @@ def setup # Attach exercise to courses to test sorting create_list(:course, 2).each { |s| s.series << create(:series, exercises: [@instance]) } get info_activity_url(@instance) + assert_response :success end @@ -48,6 +54,7 @@ def setup stub_all_activities! @instance.update(programming_language: nil) get info_activity_url(@instance) + assert_response :success end @@ -56,6 +63,7 @@ def setup Exercise.any_instance.stubs(:merged_config).raises(StandardError.new('ALL CAPS')) @instance.update(status: :not_valid) get info_activity_url(@instance) + assert_response :success end @@ -68,6 +76,7 @@ def setup end rescue ActiveRecord::RecordNotFound get activity_url(not_id) + assert_redirected_to activities_path assert_equal flash[:alert], I18n.t('activities.show.not_found') end @@ -79,7 +88,7 @@ def setup get media_activity_url(@instance, 'icon.png') assert_response :success - assert_equal response.content_type, 'image/png' + assert_equal('image/png', response.content_type) assert_equal 'bytes', response.headers['accept-ranges'] end @@ -89,7 +98,7 @@ def setup get media_activity_url(@instance, 'icon.png'), headers: { range: 'bytes=150-500' } assert_response :success - assert_equal response.content_type, 'image/png' + assert_equal('image/png', response.content_type) assert_equal 351, response.content_length assert_equal 'bytes', response.headers['accept-ranges'] end @@ -122,7 +131,7 @@ def setup get media_exercise_url(@instance, 'icon.png') assert_response :success - assert_equal response.content_type, 'image/png' + assert_equal('image/png', response.content_type) end test 'should not get private media' do @@ -136,7 +145,7 @@ def setup end test 'should get media with token on sandbox_host' do - @instance = create(:exercise, :description_html) + @instance = create :exercise, :description_html sign_out :user Exercise.any_instance.stubs(:media_path).returns(Pathname.new('public')) @instance.update access: :private @@ -156,6 +165,7 @@ def setup test 'should get activities by repository_id' do get activities_url repository_id: @instance.repository.id + assert_response :success end @@ -163,23 +173,28 @@ def setup start_exercises = Activity.exercises.count start_content = Activity.content_pages.count get activities_url(format: :json, type: ContentPage.name) + assert_equal start_content, response.parsed_body.count get activities_url(format: :json, type: Exercise.name) + assert_equal start_exercises, response.parsed_body.count end test 'should get activities with certain description languages available' do - @instance = create(:exercise, :description_html) + @instance = create :exercise, :description_html # @instance has a Dutch and Englisch description get activities_url(format: :json, description_languages: ['en']) + assert_equal 1, response.parsed_body.count assert_equal @instance.id, response.parsed_body[0]['id'] get activities_url(format: :json, description_languages: ['nl']) + assert_equal 1, response.parsed_body.count assert_equal @instance.id, response.parsed_body[0]['id'] get activities_url(format: :json, description_languages: %w[en nl]) + assert_equal 1, response.parsed_body.count assert_equal @instance.id, response.parsed_body[0]['id'] @@ -189,20 +204,25 @@ def setup create :exercise, description_nl_present: true, description_en_present: true get activities_url(format: :json, description_languages: ['nl']) + assert_equal 3, response.parsed_body.count get activities_url(format: :json, description_languages: ['en']) + assert_equal 3, response.parsed_body.count get activities_url(format: :json, description_languages: []) + assert_equal Exercise.count, response.parsed_body.count # should yield all exercises end test 'should get activities filtered by judge' do judge = @instance.judge get activities_url(format: :json, judge_id: judge.id) + assert_equal Activity.where(judge: judge).count, response.parsed_body.count assert_equal @instance.id, response.parsed_body[0]['id'] get activities_url(format: :json, judge_id: Judge.all.last.id + 1) + assert_equal 0, response.parsed_body.count end @@ -246,6 +266,7 @@ def setup assert_response :success result_exercises = response.parsed_body + assert_equal 0, result_exercises.count, 'should not contain exercises' label.activities << @instance @@ -276,6 +297,7 @@ def setup assert_response :success result_exercises = response.parsed_body + assert_equal 0, result_exercises.count, 'should not contain exercises' @instance.update(programming_language: programming_language) @@ -306,6 +328,7 @@ def setup def assert_response_contains_activity(activity, msg = nil) assert_response :success result_activities = response.parsed_body + assert result_activities.any? { |ex| ex['id'] == activity.id }, msg end @@ -316,6 +339,7 @@ def assert_response_contains_activity(activity, msg = nil) get activity_url(@instance), params: { edit_submission: submission.id } + assert_response :success end @@ -326,6 +350,7 @@ def assert_response_contains_activity(activity, msg = nil) get activity_url(@instance), params: { from_solution: 'test' } + assert_response :success end @@ -336,6 +361,7 @@ def assert_response_contains_activity(activity, msg = nil) get activity_url(@instance, format: :json), params: { from_solution: 'test' } + assert_response :forbidden end @@ -350,40 +376,45 @@ def assert_response_contains_activity(activity, msg = nil) assert_response :success exercises_response = response.parsed_body + assert_equal 2, exercises_response.count exercise_response_ids = exercises_response.pluck('id') + exercises_in_series.each do |exercise_expected| assert_includes exercise_response_ids, exercise_expected.id end end test 'should get plaintext activity media with charset=utf-8' do - @instance = create(:exercise, :description_html) + @instance = create :exercise, :description_html Exercise.any_instance.stubs(:media_path).returns(Pathname.new('public')) get("#{activity_url(@instance)}/media/robots.txt") assert_response :success - assert_equal response.content_type, 'text/plain; charset=utf-8' + assert_equal('text/plain; charset=utf-8', response.content_type) end test 'should retrieve input serviceworker script' do - @instance = create(:exercise) + @instance = create :exercise get input_service_worker_activity_path(@instance) + assert_response :success - assert_equal response.content_type, 'text/javascript' + assert_equal('text/javascript', response.content_type) series = create :series series.exercises << @instance get course_activity_input_service_worker_path(series.course, @instance) + assert_response :success - assert_equal response.content_type, 'text/javascript' + assert_equal('text/javascript', response.content_type) get course_series_activity_input_service_worker_path(series.course, series, @instance) + assert_response :success - assert_equal response.content_type, 'text/javascript' + assert_equal('text/javascript', response.content_type) end end @@ -402,12 +433,14 @@ def show_activity test 'user should be able to see activity' do @instance = exercises(:python_exercise) show_activity + assert_response :success end test 'user should not be able to see invalid activity' do @instance = create :exercise, :nameless show_activity + assert_redirected_to root_url end @@ -415,6 +448,7 @@ def show_activity @instance = create :exercise, :nameless create :submission, exercise: @instance, user: @user show_activity + assert_response :success end @@ -422,6 +456,7 @@ def show_activity sign_in users(:staff) @instance = create :exercise, :nameless show_activity + assert_response :success end @@ -429,6 +464,7 @@ def show_activity sign_out :user @instance = create :exercise, access: 'private' show_activity + assert_redirected_to sign_in_url end @@ -436,17 +472,20 @@ def show_activity sign_out :user @instance = exercises(:python_exercise) show_activity + assert_response :success end test 'authenticated user should not be able to see private activity within series' do @instance = create :exercise, access: 'private' show_activity + assert_redirected_to root_url series = create :series series.exercises << @instance get course_activity_path(series.course, @instance).concat('/') + assert_redirected_to root_url end @@ -454,6 +493,7 @@ def show_activity @instance = create :exercise, access: 'private' @instance.repository.admins << @user show_activity + assert_response :success end @@ -463,6 +503,7 @@ def show_activity sign_in users(:zeus) (create :series, course: course).exercises << instance get course_activity_path(course, instance) + assert_response :success end @@ -473,6 +514,7 @@ def show_activity series.course.subscribed_members << @user @instance.repository.allowed_courses << series.course get course_activity_path(series.course, @instance).concat('/') + assert_response :success end @@ -483,6 +525,7 @@ def show_activity series.course.subscribed_members << @user @instance.repository.allowed_courses << series.course get course_activity_path(series.course, @instance).concat('/') + assert_redirected_to root_url end @@ -493,7 +536,7 @@ def show_activity get("#{activity_url(@instance)}/media/icon.png") assert_response :success - assert_equal response.content_type, 'image/png' + assert_equal('image/png', response.content_type) end test 'should get activity media because user has submissions' do @@ -505,11 +548,11 @@ def show_activity get("#{activity_url(@instance)}/media/icon.png") assert_response :success - assert_equal response.content_type, 'image/png' + assert_equal('image/png', response.content_type) end test 'should get media of private activity in course' do - @instance = create(:exercise, :description_html, access: 'private') + @instance = create :exercise, :description_html, access: 'private' series = create :series, visibility: :hidden series.exercises << @instance series.course.enrolled_members << @user @@ -517,12 +560,13 @@ def show_activity Exercise.any_instance.stubs(:media_path).returns(Pathname.new('public')) get course_series_activity_url(series.course, series, @instance) + assert_response :success, 'should have access to activity' get("#{course_series_activity_url(series.course, series, @instance)}media/icon.png") assert_response :success, 'should have access to activity media' - assert_equal response.content_type, 'image/png' + assert_equal('image/png', response.content_type) end test 'should get redirected from activity media to root_url because user has no submissions and activity is not ok' do @@ -546,7 +590,7 @@ def show_activity end test 'should not have access to activity media when user has no access to private activity' do - @instance = create(:exercise, :description_html, access: :private) + @instance = create :exercise, :description_html, access: :private Exercise.any_instance.stubs(:media_path).returns(Pathname.new('public')) get("#{activity_url(@instance)}media/icon.png") @@ -564,7 +608,7 @@ def show_activity test 'should access private exercise media on default host with token' do sign_out :user - @instance = create(:exercise, :description_html, access: :private) + @instance = create :exercise, :description_html, access: :private Exercise.any_instance.stubs(:media_path).returns(Pathname.new('public')) get(activity_url(@instance) + "media/icon.png?token=#{@instance.access_token}") @@ -584,6 +628,7 @@ def create_exercises_return_valid get activities_url, params: { format: :json } exercises = response.parsed_body + assert_equal start_activities + 1, exercises.length assert_includes exercises.pluck('id'), visible.id end @@ -597,6 +642,7 @@ def create_exercises_return_valid get activities_url, params: { format: :json } exercises = response.parsed_body + assert_equal 3, exercises.length - start end @@ -611,16 +657,19 @@ def create_exercises_return_valid create :correct_submission, user: @user, course: s1.course, exercise: ex get activity_url(ex, format: :json) resp = response.parsed_body + assert resp['last_solution_is_best'] assert resp['has_solution'] assert resp['has_correct_solution'] get course_series_activity_url(s1.course, s1, ex, format: :json) resp = response.parsed_body + assert resp['last_solution_is_best'] assert resp['has_solution'] assert resp['has_correct_solution'] get course_series_activity_url(s2.course, s2, ex, format: :json) resp = response.parsed_body + assert resp['last_solution_is_best'] assert_not resp['has_solution'] assert_not resp['has_correct_solution'] @@ -637,18 +686,22 @@ def create_exercises_return_valid create :activity_read_state, activity: ra, user: @user, course: s1.course get activity_url(ra, format: :json) resp = response.parsed_body + assert resp['has_read'] get course_series_activity_url(s1.course, s1, ra, format: :json) resp = response.parsed_body + assert resp['has_read'] get course_series_activity_url(s2.course, s2, ra, format: :json) resp = response.parsed_body + assert_not resp['has_read'] end test 'should be able to acess index when not signed in' do sign_out @user get activities_url + assert_response :success end end @@ -743,8 +796,8 @@ class ExerciseDescriptionTest < ActionDispatch::IntegrationTest exercise_json = response.parsed_body description_url = exercise_json['description_url'] - assert description_url.include?(Rails.configuration.sandbox_host) - assert description_url.include?(exercise.access_token) + assert_includes description_url, Rails.configuration.sandbox_host + assert_includes description_url, exercise.access_token end test 'json link to description should return a 406' do @@ -766,6 +819,7 @@ class ExerciseDescriptionTest < ActionDispatch::IntegrationTest assert_response :success activities_json = response.parsed_body + assert_equal [exercise.id, other_exercise.id], activities_json.pluck('id') SeriesMembership.find_by(series: series, activity: exercise).update(order: 2) @@ -775,6 +829,7 @@ class ExerciseDescriptionTest < ActionDispatch::IntegrationTest assert_response :success activities_json = response.parsed_body + assert_equal [other_exercise.id, exercise.id], activities_json.pluck('id') end @@ -788,6 +843,7 @@ class ExerciseDescriptionTest < ActionDispatch::IntegrationTest assert_response :success activities_json = response.parsed_body + assert_equal [exercise.id, other_exercise.id], activities_json.pluck('id') create :series, exercises: [other_exercise] @@ -797,6 +853,7 @@ class ExerciseDescriptionTest < ActionDispatch::IntegrationTest assert_response :success activities_json = response.parsed_body + assert_equal [other_exercise.id, exercise.id], activities_json.pluck('id') end end diff --git a/test/controllers/activity_read_states_controller_test.rb b/test/controllers/activity_read_states_controller_test.rb index 0c02277626..21defab52f 100644 --- a/test/controllers/activity_read_states_controller_test.rb +++ b/test/controllers/activity_read_states_controller_test.rb @@ -18,17 +18,20 @@ def setup series = create :series, course: course series.exercises << a1 get course_activity_activity_read_states_url(course, a1) + assert_response :success end test 'should be able to get course scoped index page' do course = courses(:course1) get course_activity_read_states_url course + assert_response :success end test 'should be able to get user scoped index page' do get user_activity_read_states_url @user + assert_response :success end @@ -96,31 +99,35 @@ def setup cp = create :content_page create :activity_read_state, activity: cp, user: @user post activity_activity_read_states_url(cp, format: :js), params: { activity_read_state: { activity_id: cp.id } } + assert_response :unprocessable_entity end test 'should mark content_page as read outside course' do cp = create :content_page post activity_activity_read_states_url(cp, format: :js), params: { activity_read_state: { activity_id: cp.id } } + assert_response :success - assert ActivityReadState.where(user: @user, activity: cp, course: nil).any? + assert_predicate ActivityReadState.where(user: @user, activity: cp, course: nil), :any? end test 'should mark content_page as read within course' do course = create :course, series_count: 1, content_pages_per_series: 1, subscribed_members: [@user] cp = course.series.first.content_pages.first post activity_activity_read_states_url(cp, format: :js), params: { activity_read_state: { activity_id: cp.id, course_id: course.id } } + assert_response :success - assert ActivityReadState.where(user: @user, activity: cp, course: course).any? + assert_predicate ActivityReadState.where(user: @user, activity: cp, course: course), :any? end test 'should mark content_page as read as json' do cp = create :content_page post activity_activity_read_states_url(cp, format: :json), params: { activity_read_state: { activity_id: cp.id } } + assert_response :success - assert ActivityReadState.where(user: @user, activity: cp, course: nil).any? + assert_predicate ActivityReadState.where(user: @user, activity: cp, course: nil), :any? end end diff --git a/test/controllers/annotations_controller_test.rb b/test/controllers/annotations_controller_test.rb index 1edd92053f..59e6ad9464 100644 --- a/test/controllers/annotations_controller_test.rb +++ b/test/controllers/annotations_controller_test.rb @@ -49,6 +49,7 @@ def setup Question.per_page = 1 create_list :question, 2, submission: submission get questions_url, params: { everything: true } + assert_select 'a[href=?]', questions_path(page: 2, everything: true) end @@ -149,14 +150,17 @@ def setup create :annotation, user: other_user, submission: (create :submission, user: other_user, course: create(:course)) get annotations_url(format: :json) + assert_equal 5, response.parsed_body.count sign_in course_admin get annotations_url(format: :json) + assert_equal 3, response.parsed_body.count sign_in user get annotations_url(format: :json) + assert_equal 3, response.parsed_body.count end @@ -171,9 +175,11 @@ def setup create :annotation, user: other_user, submission: (create :submission, user: other_user, course: create(:course)) get annotations_url(format: :json, user_id: user.id) + assert_equal 3, response.parsed_body.count get annotations_url(format: :json, user_id: other_user.id) + assert_equal 2, response.parsed_body.count end @@ -182,6 +188,7 @@ def setup sign_in @submission.user get annotation_url(annotation, format: :json) + assert_response :success end @@ -190,6 +197,7 @@ def setup sign_in create(:user) get annotation_url(annotation, format: :json) + assert_response :forbidden end @@ -202,6 +210,7 @@ def setup }, format: :json } + assert_response :success patch annotation_url(annotation), params: { @@ -210,12 +219,14 @@ def setup }, format: :json } + assert_response :success end test 'can remove annotation' do annotation = create :annotation, submission: @submission, user: @zeus delete annotation_url(annotation) + assert_response :no_content end @@ -227,6 +238,7 @@ def setup }, format: :json } + assert_response :unprocessable_entity end @@ -263,6 +275,7 @@ def setup saved_annotation_id: sa.id } } + assert_response :success end end @@ -285,6 +298,7 @@ def setup }, format: :json } + assert_response :unauthorized end @@ -296,8 +310,9 @@ def setup }, format: :json } + assert_response :created - assert @submission.questions.any? + assert_predicate @submission.questions, :any? end test 'student cannot create a question if disabled for course' do @@ -312,9 +327,10 @@ def setup }, format: :json } + assert_response :forbidden - assert_equal submission.user.questions.count, 0, 'Student is not allowed to create questions' + assert_equal(0, submission.user.questions.count, 'Student is not allowed to create questions') end test 'student cannot create question if no course' do @@ -328,9 +344,10 @@ def setup }, format: :json } + assert_response :forbidden - assert_equal submission.user.questions.count, 0, 'Student is not allowed to create questions without course' + assert_equal(0, submission.user.questions.count, 'Student is not allowed to create questions without course') end test 'random user cannot create question' do @@ -342,6 +359,7 @@ def setup }, format: :json } + assert_response :forbidden end @@ -354,6 +372,7 @@ def setup }, format: :json } + assert_response :created assert_not @submission.questions.any? end @@ -367,6 +386,7 @@ def setup }, format: :json } + assert_response :forbidden end @@ -381,6 +401,7 @@ def setup }, format: :json } + assert_response :created assert_not @submission.questions.any? end @@ -394,6 +415,7 @@ def setup }, format: :json } + assert_response :forbidden end @@ -418,6 +440,7 @@ def setup }, format: :json } + assert_response valid ? :ok : :forbidden # Unanswered -> answered @@ -429,6 +452,7 @@ def setup }, format: :json } + assert_response valid ? :ok : :forbidden # Unanswered -> unanswered @@ -440,6 +464,7 @@ def setup }, format: :json } + assert_response :forbidden sign_out user @@ -456,6 +481,7 @@ def setup }, format: :json } + assert_response :ok # Answered -> answered @@ -467,6 +493,7 @@ def setup }, format: :json } + assert_response :forbidden # without delayed jobs, in progress is automatically reset to unanswered @@ -480,6 +507,7 @@ def setup }, format: :json } + assert_response :ok end run_delayed_jobs @@ -506,6 +534,7 @@ def setup }, format: :json } + assert_response :forbidden # In progress -> answered @@ -517,6 +546,7 @@ def setup }, format: :json } + assert_response valid ? :ok : :forbidden # In progress -> unanswered @@ -528,6 +558,7 @@ def setup }, format: :json } + assert_response valid ? :ok : :forbidden sign_out user @@ -545,6 +576,7 @@ def setup }, format: :json } + assert_response :unauthorized end @@ -559,6 +591,7 @@ def setup }, format: :json } + assert_response :forbidden patch annotation_path(question), params: { @@ -567,6 +600,7 @@ def setup }, format: :json } + assert_response :ok question = create :question, submission: @submission, question_state: :answered @@ -577,6 +611,7 @@ def setup }, format: :json } + assert_response :forbidden patch annotation_path(question), params: { @@ -585,6 +620,7 @@ def setup }, format: :json } + assert_response :ok end @@ -608,6 +644,7 @@ def setup }, format: :json } + assert_response valid ? :ok : :forbidden # Answered -> answered @@ -619,6 +656,7 @@ def setup }, format: :json } + assert_response :forbidden # Answered -> unanswered @@ -630,6 +668,7 @@ def setup }, format: :json } + assert_response valid ? :ok : :forbidden sign_out user @@ -640,11 +679,13 @@ def setup question = create :question, submission: @submission, question_state: :answered delete annotation_url(question) + assert_not response.successful? question = create :question, submission: @submission, question_state: :unanswered delete annotation_url(question) + assert_response :no_content end @@ -657,6 +698,7 @@ def setup }, format: :json } + assert_response :success question = create :question, submission: @submission, question_state: :unanswered @@ -667,6 +709,7 @@ def setup }, format: :json } + assert_response :success end end diff --git a/test/controllers/announcement_controller_test.rb b/test/controllers/announcement_controller_test.rb index 14bf33bb26..f238b0f7c8 100644 --- a/test/controllers/announcement_controller_test.rb +++ b/test/controllers/announcement_controller_test.rb @@ -14,6 +14,7 @@ class AnnouncementControllerTest < ActionDispatch::IntegrationTest test 'create should redirect to index' do create_request_expect + assert_redirected_to announcements_url end @@ -26,9 +27,11 @@ def announcement?(user = nil) test 'Mark as read should work' do student = create :student + assert announcement? student sign_in student post mark_as_read_announcement_url @instance, format: :js + assert_not announcement? student end @@ -36,6 +39,7 @@ def announcement?(user = nil) @instance.update institution_id: 1 a = create :student, institution_id: 1 b = create :student, institution_id: (create :institution).id + assert announcement? a assert_not announcement? b end @@ -44,6 +48,7 @@ def announcement?(user = nil) @instance.update institution_id: 1 a = create :staff, institution_id: 1 b = create :staff, institution_id: (create :institution).id + assert announcement? a assert_not announcement? b end @@ -54,30 +59,35 @@ def announcement?(user = nil) zeus = create :zeus @instance.update user_group: :everyone + assert announcement? student assert announcement? staff assert announcement? zeus - assert announcement? + assert_predicate self, :announcement? @instance.update user_group: :all_users + assert announcement? student assert announcement? staff assert announcement? zeus assert_not announcement? @instance.update user_group: :students + assert announcement? student assert_not announcement? staff assert announcement? zeus assert_not announcement? @instance.update user_group: :staff + assert_not announcement? student assert announcement? staff assert announcement? zeus assert_not announcement? @instance.update user_group: :zeus + assert_not announcement? student assert_not announcement? staff assert announcement? zeus @@ -87,25 +97,32 @@ def announcement?(user = nil) test 'only active announcements should be shown' do student = create :student @instance.update start_delivering_at: 1.day.ago + assert announcement? student @instance.update start_delivering_at: 1.day.from_now + assert_not announcement? student @instance.update start_delivering_at: nil @instance.update stop_delivering_at: 1.day.from_now + assert announcement? student @instance.update stop_delivering_at: 1.day.ago + assert_not announcement? student @instance.update start_delivering_at: 1.day.ago, stop_delivering_at: 1.day.from_now + assert announcement? student @instance.update start_delivering_at: 2.days.ago, stop_delivering_at: 1.day.ago + assert_not announcement? student @instance.update start_delivering_at: 1.day.from_now, stop_delivering_at: 2.days.from_now + assert_not announcement? student end @@ -115,11 +132,14 @@ def announcement?(user = nil) sign_in student post mark_as_read_announcement_url @instance, format: :js end + assert_equal 5, @instance.announcement_views.count sign_in create :zeus put announcement_url @instance, announcement: { text_nl: 'a', text_en: 'b' } + assert_equal 5, @instance.announcement_views.count put announcement_url @instance, announcement: { text_nl: 'a', text_en: 'b' }, reset_announcement_views: true + assert_equal 0, @instance.announcement_views.count end end diff --git a/test/controllers/api_tokens_controller_test.rb b/test/controllers/api_tokens_controller_test.rb index 917dbcab76..cec8dc4576 100644 --- a/test/controllers/api_tokens_controller_test.rb +++ b/test/controllers/api_tokens_controller_test.rb @@ -21,6 +21,7 @@ def create_request(attr_hash: nil) test 'should get index for user' do get user_api_tokens_url(@instance.user), params: { format: :json } + assert_response :success end @@ -55,14 +56,17 @@ def fetch_root_with_token(token) test 'should login with token' do fetch_root_with_token(@token) + assert_response :success result = response.parsed_body + assert_not_nil result['user'] assert_equal result['user']['email'], @user.email end test 'should not login with wrong token' do fetch_root_with_token('Not a correct token') + assert_response :unauthorized end end diff --git a/test/controllers/application_controller_test.rb b/test/controllers/application_controller_test.rb index 876e5c5d5e..411c93c3a1 100644 --- a/test/controllers/application_controller_test.rb +++ b/test/controllers/application_controller_test.rb @@ -8,6 +8,7 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest test 'should store last location' do location = root_path params: { foo: 'bar' } get location + assert_response :success assert_equal session[:user_return_to], location end @@ -15,6 +16,7 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest test 'should not store last location if too big' do location = root_path params: { foo: "b#{'a' * 1024}r" } get location + assert_response :success assert_not_equal session[:user_return_to], location assert session[:user_return_to].length < 100 @@ -22,18 +24,21 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest test 'should get unauthorized status when not logged in' do get course_url(@course, format: :json) + assert_response :unauthorized end test 'should get forbidden status when logged in but not authorized' do sign_in @user get course_url(@course, format: :json) + assert_response :forbidden end test 'page zero should return page one' do sign_in @user get courses_path(page: 0) + assert_response :ok end end diff --git a/test/controllers/auth/authentication_controller_test.rb b/test/controllers/auth/authentication_controller_test.rb index 5ef5956f56..8cf23464ba 100644 --- a/test/controllers/auth/authentication_controller_test.rb +++ b/test/controllers/auth/authentication_controller_test.rb @@ -5,6 +5,7 @@ class PagesControllerTest < ActionDispatch::IntegrationTest test 'should get sign in page' do get sign_in_url + assert_response :success assert_template 'auth/sign_in' end diff --git a/test/controllers/auth/omniauth_callbacks_controller_test.rb b/test/controllers/auth/omniauth_callbacks_controller_test.rb index 24980a799b..05f2e8cc2d 100644 --- a/test/controllers/auth/omniauth_callbacks_controller_test.rb +++ b/test/controllers/auth/omniauth_callbacks_controller_test.rb @@ -240,6 +240,7 @@ def omniauth_path(provider) follow_redirect! user.reload + assert_equal user, @controller.current_user assert_equal 'Flip', user.first_name assert_equal 'Flapstaart', user.last_name @@ -268,9 +269,11 @@ def omniauth_path(provider) end user.reload + assert_redirected_to root_path assert_nil @controller.current_user user.reload + assert_not_equal other_user.email, user.email # Cleanup. @@ -462,7 +465,8 @@ def omniauth_path(provider) assert_equal @controller.current_user, user identity.reload - assert_equal identity.identifier, 'NEW-UID' + + assert_equal('NEW-UID', identity.identifier) # Cleanup. sign_out user @@ -498,7 +502,8 @@ def omniauth_path(provider) assert_redirected_to root_path assert_not_equal @controller.current_user, user identity.reload - assert_equal identity.identifier, 'NEW-UID' + + assert_equal('NEW-UID', identity.identifier) # Cleanup. sign_out user @@ -523,7 +528,8 @@ def omniauth_path(provider) assert_equal @controller.current_user, user identity.reload - assert_equal identity.identifier, 'NEW-UID' + + assert_equal('NEW-UID', identity.identifier) # Cleanup. sign_out user @@ -545,7 +551,8 @@ def omniauth_path(provider) assert_equal @controller.current_user, user identity.reload - assert_equal identity.identifier, 'NEW-UID' + + assert_equal('NEW-UID', identity.identifier) # Cleanup. sign_out user @@ -567,7 +574,8 @@ def omniauth_path(provider) assert_equal @controller.current_user, user identity.reload - assert_equal identity.identifier, 'NEW-UID' + + assert_equal('NEW-UID', identity.identifier) # Cleanup. sign_out user @@ -603,7 +611,8 @@ def omniauth_path(provider) assert_redirected_to root_path assert_not_equal @controller.current_user, user identity.reload - assert_equal identity.identifier, 'NEW-UID' + + assert_equal('NEW-UID', identity.identifier) # Cleanup. sign_out user @@ -626,7 +635,8 @@ def omniauth_path(provider) assert_equal @controller.current_user, user identity.reload - assert_equal identity.identifier, 'NEW-UID' + + assert_equal('NEW-UID', identity.identifier) # Cleanup. sign_out user @@ -650,7 +660,8 @@ def omniauth_path(provider) assert_equal @controller.current_user, user identity.reload - assert_equal identity.identifier, 'NEW-UID' + + assert_equal('NEW-UID', identity.identifier) # Cleanup. sign_out user @@ -701,6 +712,7 @@ def omniauth_path(provider) # Test "inside iframe" post omniauth_url(provider) follow_redirect! + assert_redirected_to lti_redirect_path(provider: main_provider.id, sym: main_provider.class.sym) # Test outside iframe @@ -708,6 +720,7 @@ def omniauth_path(provider) follow_redirect!(headers: { 'Sec-Fetch-Dest' => 'document' }) + assert_redirected_to omniauth_url(main_provider) end diff --git a/test/controllers/auth/saml_controller_test.rb b/test/controllers/auth/saml_controller_test.rb index f698ca5d66..247d2cd664 100644 --- a/test/controllers/auth/saml_controller_test.rb +++ b/test/controllers/auth/saml_controller_test.rb @@ -4,6 +4,7 @@ class SamlControllerTest < ActionDispatch::IntegrationTest test 'SAML metadata' do # Validate whether the request works. get users_saml_metadata_path + assert_response :success # Validate that actual metadata is returned. diff --git a/test/controllers/courses_controller_test.rb b/test/controllers/courses_controller_test.rb index e8ab7e8c1f..935892b1cd 100644 --- a/test/controllers/courses_controller_test.rb +++ b/test/controllers/courses_controller_test.rb @@ -17,6 +17,7 @@ class CoursesControllerTest < ActionDispatch::IntegrationTest @instance.series << create(:series) @instance.series.first.activities << create(:exercise, access: :private) get course_url(@instance) + assert_response :success end @@ -25,6 +26,7 @@ class CoursesControllerTest < ActionDispatch::IntegrationTest @instance.series.first.activities << create(:exercise, access: :private) sign_out :user get course_url(@instance) + assert_response :success end @@ -35,12 +37,14 @@ class CoursesControllerTest < ActionDispatch::IntegrationTest @instance.administrating_members << user sign_in user get manage_series_course_url(@instance) + assert_response :success end test 'should reset token' do old_secret = @instance.secret post reset_token_course_url(@instance) + assert_not_equal old_secret, @instance.reload.secret end end @@ -143,7 +147,8 @@ def with_users_signed_in(users) add_not_subscribed with_users_signed_in @not_subscribed.compact do |who, user| post subscribe_course_url(@course, format: :json) - assert @course.subscribed_members.include?(user), "#{who} should be able to subscribe" + + assert_includes @course.subscribed_members, user, "#{who} should be able to subscribe" end end @@ -151,6 +156,7 @@ def with_users_signed_in(users) add_not_subscribed with_users_signed_in @not_subscribed.compact do |who| post subscribe_course_url(@course) + assert_redirected_to @course, "#{who} should be redirected" end end @@ -161,6 +167,7 @@ def with_users_signed_in(users) %w[visible_for_all visible_for_institution hidden].product(%w[open_for_all open_for_institutional_users open_for_institution closed], [true, false]).each do |v, r, m| @course.update(visibility: v, registration: r, moderated: m) get course_url(@course, secret: @course.secret, format: :json) + assert_response :success, "#{who} should get registration page" # GET should not subscribe assert_not user.member_of?(@course), "#{who} should not be registered" @@ -170,6 +177,7 @@ def with_users_signed_in(users) test 'should not subscribe when already subscribed' do full_setup + with_users_signed_in @course.users do |who| assert_difference('CourseMembership.count', 0, "#{who} should not be able to create a second membership") do post subscribe_course_url(@course, format: :json) @@ -182,7 +190,8 @@ def with_users_signed_in(users) with_users_signed_in @unsubscribed do |who, user| assert_not user.member_of?(@course), "#{who} was already a member" post subscribe_course_url(@course, format: :json) - assert user.courses.include?(@course), "#{who} should be a member" + + assert_includes user.courses, @course, "#{who} should be a member" end end @@ -190,12 +199,15 @@ def with_users_signed_in(users) add_admins sign_in @course_admins.first get scoresheet_course_url(@course) + assert_response :success, 'course_admin should be able to get course scoresheet' get scoresheet_course_url(@course, format: :csv) + assert_response :success, 'course_admin should be able to get course scoresheet' get scoresheet_course_url(@course, format: :json) + assert_response :success, 'course_admin should be able to get course scoresheet' end @@ -203,6 +215,7 @@ def with_users_signed_in(users) add_students sign_in @students.first get scoresheet_course_url(@course) + assert_response :redirect, 'student should not be able to get course scoresheet' end @@ -211,6 +224,7 @@ def with_users_signed_in(users) add_subscribed with_users_signed_in @subscribed do |who| get registration_course_url(@course, @course.secret, format: :json) + assert_redirected_to @course, "#{who} should be redirected" end end @@ -223,6 +237,7 @@ def with_users_signed_in(users) sign_in zeus get registration_course_url(@course, @course.secret) + assert_redirected_to @course, 'zeus should be redirected' end @@ -231,12 +246,15 @@ def with_users_signed_in(users) add_not_subscribed with_users_signed_in @not_subscribed.compact do |who, user| post subscribe_course_url(@course, secret: 'the cake is a lie') + assert_not user.member_of?(@course), "#{who} with invalid secret" post subscribe_course_url(@course, secret: '') + assert_not user.member_of?(@course), "#{who} with empty secret" post subscribe_course_url(@course) + assert_not user.member_of?(@course), "#{who} without secret" end end @@ -247,10 +265,12 @@ def with_users_signed_in(users) with_users_signed_in @not_subscribed.compact do |who, user| @course.update(visibility: 'visible_for_all') post subscribe_course_url(@course) + assert_not user.member_of?(@course), "#{who} should not be subscribed" @course.update(visibility: 'hidden') post subscribe_course_url(@course, secret: @course.secret) + assert_not user.member_of?(@course), "#{who} should not be subscribed" end end @@ -261,10 +281,12 @@ def with_users_signed_in(users) with_users_signed_in @externals do |who, user| @course.update(visibility: 'visible_for_all') post subscribe_course_url(@course) + assert_not @course.users.include?(user), "#{who} should not have a membership" @course.update(visibility: 'hidden') post subscribe_course_url(@course, secret: @course.secret) + assert_not @course.users.include?(user), "#{who} should not have a membership" end end @@ -274,7 +296,8 @@ def with_users_signed_in(users) add_not_subscribed with_users_signed_in @not_subscribed.compact do |who, user| post subscribe_course_url(@course, format: :json) - assert @course.pending_members.include?(user), "#{who} should be pending" + + assert_includes @course.pending_members, user, "#{who} should be pending" end end @@ -283,6 +306,7 @@ def with_users_signed_in(users) add_pending with_users_signed_in @pending do |who, user| post unsubscribe_course_url(@course, format: :json) + assert_not @course.pending_members.include?(user), "#{who} should not be pending anymore" end end @@ -292,7 +316,8 @@ def with_users_signed_in(users) add_not_subscribed with_users_signed_in @not_subscribed.compact do |who, user| post subscribe_course_url(@course, secret: @course.secret, format: :json) - assert @course.pending_members.include?(user), "#{who} should be pending" + + assert_includes @course.pending_members, user, "#{who} should be pending" end end @@ -300,6 +325,7 @@ def with_users_signed_in(users) add_externals with_users_signed_in(@externals + [nil]) do |who| get course_url(@course) + assert_response :success, "#{who} should be able to see course" end end @@ -327,9 +353,11 @@ def with_users_signed_in(users) @course.pending_members << acceptme << declineme post update_membership_course_url(@course, user: acceptme, status: 'student') - assert @course.enrolled_members.include?(acceptme), "#{who} student not enrolled" + + assert_includes @course.enrolled_members, acceptme, "#{who} student not enrolled" post update_membership_course_url(@course, user: declineme, status: 'unsubscribed') + assert_not @course.enrolled_members.include?(declineme), "#{who} student not unsubscribed" end end @@ -341,10 +369,12 @@ def with_users_signed_in(users) students = create_list :student, 2 @course.pending_members = students post mass_accept_pending_course_url(@course), params: { format: :json } + assert_response 200 @course.reload - assert @course.pending_members.empty?, "#{who} should be able to accept pending" + + assert_empty @course.pending_members, "#{who} should be able to accept pending" assert (students - @course.enrolled_members), "students should be enrolled for #{who}" end end @@ -360,13 +390,15 @@ def with_users_signed_in(users) @course.pending_members << submission.user post mass_decline_pending_course_url(@course), params: { format: :json } + assert_response 200 @course.reload - assert @course.pending_members.empty?, "#{who} should be able to decline pending" - assert CourseMembership.where(course: @course, user: students).empty?, "memberships should be deleted for #{who}" - assert @course.unsubscribed_members.include?(submission.user) + assert_empty @course.pending_members, "#{who} should be able to decline pending" + assert_empty CourseMembership.where(course: @course, user: students), "memberships should be deleted for #{who}" + + assert_includes @course.unsubscribed_members, submission.user end end @@ -379,9 +411,11 @@ def with_users_signed_in(users) @course.pending_members << acceptme << declineme post update_membership_course_url(@course, user: acceptme, status: 'student') + assert_not @course.enrolled_members.include?(acceptme), "#{who} student should not be enrolled" post update_membership_course_url(@course, user: acceptme, status: 'unsubscribed') + assert_not @course.unsubscribed_members.include?(acceptme), "#{who} student should not be unsubscribed" end end @@ -395,10 +429,12 @@ def with_users_signed_in(users) @course.enrolled_members.concat members members.each do |u| post update_membership_course_url(@course, user: u, status: 'course_admin') - assert @course.reload.administrating_members.include?(u), "#{who} should be able to promote members" + + assert_includes @course.reload.administrating_members, u, "#{who} should be able to promote members" post update_membership_course_url(@course, user: u, status: 'student') - assert @course.reload.enrolled_members.include?(u), "#{who} should be able to demote members" + + assert_includes @course.reload.enrolled_members, u, "#{who} should be able to demote members" end end end @@ -413,11 +449,13 @@ def with_users_signed_in(users) @course.administrating_members.concat members_admin members_student.each do |u| post update_membership_course_url(@course, user: u, status: 'course_admin') - assert @course.reload.administrating_members.include?(u), "#{who} should be able to promote members" + + assert_includes @course.reload.administrating_members, u, "#{who} should be able to promote members" end members_admin.each do |u| post update_membership_course_url(@course, user: u, status: 'student') - assert @course.reload.enrolled_members.include?(u), "#{who} should be able to demote members" + + assert_includes @course.reload.enrolled_members, u, "#{who} should be able to demote members" end end with_users_signed_in @not_admins do |who| @@ -427,10 +465,12 @@ def with_users_signed_in(users) @course.administrating_members.concat members_admin members_student.each do |u| post update_membership_course_url(@course, user: u, status: 'course_admin') + assert_not @course.reload.administrating_members.include?(u), "#{who} should not be able to promote members" end members_admin.each do |u| post update_membership_course_url(@course, user: u, status: 'student') + assert_not @course.reload.enrolled_members.include?(u), "#{who} should not be able to demote members" end end @@ -444,11 +484,12 @@ def with_users_signed_in(users) post unsubscribe_course_url(@course) - assert @course.administrating_members.include?(admin) + assert_includes @course.administrating_members, admin %w[student pending unsubscribed].each do |s| post update_membership_course_url(@course, user: admin, status: s) - assert @course.administrating_members.include?(admin) + + assert_includes @course.administrating_members, admin end end @@ -473,7 +514,8 @@ def with_users_signed_in(users) course: @course post unsubscribe_course_url(@course) - assert @course.unsubscribed_members.include?(user) + + assert_includes @course.unsubscribed_members, user end test 'unsubscribing user without solutions for course should delete membership' do @@ -482,6 +524,7 @@ def with_users_signed_in(users) sign_in user post unsubscribe_course_url(@course) + assert_not @course.users.include?(user) end @@ -489,6 +532,7 @@ def with_users_signed_in(users) add_admins with_users_signed_in @admins do |who| get course_members_url(@course), xhr: true + assert_response :success, "#{who} should be able to list members" end end @@ -497,6 +541,7 @@ def with_users_signed_in(users) add_not_admins with_users_signed_in @not_admins do |who| get course_members_url(@course), xhr: true + assert (response.forbidden? || response.unauthorized?), "#{who} should not be able to list members" end end @@ -507,6 +552,7 @@ def with_users_signed_in(users) with_users_signed_in @admins do |who| @students.each do |view| get course_member_url(@course, view), xhr: true + assert_response :success, "#{who} should be able to view #{view.permission}:#{view.membership_status_for(@course)}" end end @@ -517,6 +563,7 @@ def with_users_signed_in(users) with_users_signed_in @not_admins do |who, signed_in| @course.users.reject { |u| u == signed_in }.each do |view| get course_member_url(@course, view), xhr: true + assert (response.forbidden? || response.unauthorized?), "#{who} should not be able to view #{view}" end end @@ -528,6 +575,7 @@ def with_users_signed_in(users) with_users_signed_in @admins do |who| get courses_url, params: { format: :json } courses = response.parsed_body + assert courses.any? { |c| c['id'] == @course.id }, "#{who} should be able to see a hidden course of which he is course administrator" end end @@ -539,6 +587,7 @@ def with_users_signed_in(users) get courses_url, params: { format: :json } if response.successful? courses = response.parsed_body + assert_not courses.any? { |c| c['id'] == @course.id }, "#{who} should not be able to see a hidden course" else assert response.forbidden? || response.unauthorized? @@ -548,6 +597,7 @@ def with_users_signed_in(users) test 'signed out users should be able to see the courses listing' do get courses_url + assert_response :success # we only expect the "all courses" and "featured courses" tabs to show for signed out users assert_select 'd-filter-tabs[labels*="featured"]', 1 @@ -570,54 +620,74 @@ def with_users_signed_in(users) # all courses get courses_url, params: { format: :json } + assert_response :success courses = response.parsed_body + assert_equal 3, courses.length # my courses get courses_url, params: { format: :json, tab: 'my' } + assert_response :success courses = response.parsed_body + assert_equal 1, courses.length # institution courses get courses_url, params: { format: :json, tab: 'institution' } + assert_response :success courses = response.parsed_body + assert_equal 2, courses.length # copy courses get courses_url, params: { format: :json, copy_courses: true } + assert_response :success courses = response.parsed_body + assert_equal 3, courses.length # copy courses filtered get courses_url, params: { format: :json, copy_courses: true, filter: 'course' } + assert_response :success courses = response.parsed_body + assert_equal 2, courses.length # All courses filtered get courses_url, params: { format: :json, filter: 'greatest' } + assert_response :success courses = response.parsed_body + assert_equal 1, courses.length # Institution courses filtered get courses_url, params: { format: :json, filter: 'worst' } + assert_response :success courses = response.parsed_body + assert_equal 1, courses.length end test 'featured courses should only show featured courses' do get courses_url, params: { format: :json } + assert_response :success courses = response.parsed_body + assert_equal Course.count, courses.length get courses_url, params: { format: :json, tab: 'featured' } + assert_response :success courses = response.parsed_body + assert_equal 0, courses.length @course.update(featured: true) get courses_url, params: { format: :json, tab: 'featured' } + assert_response :success courses = response.parsed_body + assert_equal 1, courses.length end @@ -627,6 +697,7 @@ def with_users_signed_in(users) sign_in user post favorite_course_url(@course) + assert CourseMembership.find_by(user: user, course: @course).favorite end @@ -637,6 +708,7 @@ def with_users_signed_in(users) post favorite_course_url(@course) post unfavorite_course_url(@course) + assert_not CourseMembership.find_by(user: user, course: @course).favorite end @@ -647,6 +719,7 @@ def with_users_signed_in(users) post unsubscribe_course_url(@course) post favorite_course_url(@course) + assert_not response.successful? end @@ -657,6 +730,7 @@ def with_users_signed_in(users) post unsubscribe_course_url(@course) post unfavorite_course_url(@course) + assert_not response.successful? end @@ -668,8 +742,9 @@ def with_users_signed_in(users) sign_in user post subscribe_course_url(course) + assert_redirected_to course - assert course.subscribed_members.include?(user) + assert_includes course.subscribed_members, user end test 'a course copied by a regular student should not include hidden/closed series' do @@ -682,6 +757,7 @@ def with_users_signed_in(users) sign_in user new_course = build :course post courses_url, params: { course: { name: new_course.name, description: new_course.description, visibility: new_course.visibility, registration: new_course.registration, teacher: new_course.teacher }, copy_options: { base_id: course.id }, format: :json } + assert_equal 0, Course.find(response.parsed_body['id']).series.count end @@ -691,7 +767,8 @@ def with_users_signed_in(users) user = @externals.first sign_in user get course_url(@course, secret: @course.secret) - assert response.body.include?(subscribe_course_path(@course, secret: @course.secret)) + + assert_includes response.body, subscribe_course_path(@course, secret: @course.secret) end test 'visible_for_institution course page shown to unsubscribed student of different institution should include registration url with secret' do @@ -701,7 +778,8 @@ def with_users_signed_in(users) user.update(institution: (create :institution)) sign_in user get course_url(@course, secret: @course.secret) - assert response.body.include?(subscribe_course_path(@course, secret: @course.secret)) + + assert_includes response.body, subscribe_course_path(@course, secret: @course.secret) end test 'visible_for_institution course page shown to unsubscribed student of same institution should not include registration url with secret' do @@ -712,6 +790,7 @@ def with_users_signed_in(users) user.update(institution: @course.institution) sign_in user get course_url(@course, secret: @course.secret) + assert_not response.body.include?(subscribe_course_path(@course, secret: @course.secret)) end @@ -722,8 +801,10 @@ def with_users_signed_in(users) user.update(institution: nil) sign_in user post subscribe_course_url(@course) + assert_not @course.subscribed_members.include?(user) post subscribe_course_url(@course, secret: @course.secret) + assert_not @course.subscribed_members.include?(user) end @@ -734,15 +815,18 @@ def with_users_signed_in(users) user.update(institution: (create :institution)) sign_in user post subscribe_course_url(@course) - assert @course.subscribed_members.include?(user) + + assert_includes @course.subscribed_members, user post subscribe_course_url(@course, secret: @course.secret) - assert @course.subscribed_members.include?(user) + + assert_includes @course.subscribed_members, user end test 'should not destroy course as student' do add_students sign_in @students.first delete course_url(@course) + assert_not response.successful? end @@ -752,7 +836,7 @@ def with_users_signed_in(users) assert_difference 'Course.count', -1 do delete course_url(@course) end - assert response.body.include?(courses_url) + assert_includes response.body, courses_url end test 'should not destroy course as course admin if too many submissions' do @@ -763,6 +847,7 @@ def with_users_signed_in(users) # Assert there are actually too many submissions assert_operator @course.submissions.count, :>, CoursePolicy::MAX_SUBMISSIONS_FOR_DESTROY delete course_url(@course) + assert_not response.successful? end @@ -775,7 +860,7 @@ def with_users_signed_in(users) assert_difference 'Course.count', -1 do delete course_url(@course) end - assert response.body.include?(courses_url) + assert_includes response.body, courses_url end test 'super admins are able to view questions' do @@ -788,6 +873,7 @@ def with_users_signed_in(users) create :question, question_state: :unanswered, submission: submission create :question, question_state: :in_progress, submission: submission get questions_course_path(@course) + assert :ok, "#{who} should be able to view questions" end end @@ -796,6 +882,7 @@ def with_users_signed_in(users) add_not_admins with_users_signed_in @not_admins do |who| get questions_course_path(@course) + assert :ok, "#{who} should not be able to view questions" end end @@ -805,6 +892,7 @@ def with_users_signed_in(users) add_admins sign_in @admins.first get questions_course_path(@course) + assert_select 'title', /^([^0-9]*)$/ submission = create :submission, course: @course @@ -812,6 +900,7 @@ def with_users_signed_in(users) create :question, question_state: :unanswered, submission: submission create :question, question_state: :in_progress, submission: submission get questions_course_path(@course) + assert_select 'title', /\(1\)/ end end @@ -828,24 +917,29 @@ def with_users_signed_in(users) # Check content of the ics file get ical_course_url @course, format: :ics + assert_response :success assert_equal 'text/plain; charset=utf-8', response.content_type strict_parser = Icalendar::Parser.new(response.body, true) cals = strict_parser.parse cal = cals.first + assert_equal "Dodona: #{@course.name}", cal.x_wr_calname.first # Last created serie will be listed first event1 = cal.events.first + assert_equal Icalendar::Values::DateTime.new("#{time2.utc.strftime('%Y%m%dT%H%M%S')}Z"), event1.dtstart assert_equal Icalendar::Values::DateTime.new("#{time2.utc.strftime('%Y%m%dT%H%M%S')}Z"), event1.dtstart assert_equal 'open serie2 + deadline', event1.summary expected = I18n.t('courses.ical.serie_deadline', serie_name: serie2.name, course_name: @course.name, serie_url: series_url(serie2)) + assert_equal expected, event1.description assert_equal series_url(serie2), event1.url.to_s event2 = cal.events.second + assert_equal Icalendar::Values::DateTime.new("#{time1.utc.strftime('%Y%m%dT%H%M%S')}Z"), event2.dtstart assert_equal Icalendar::Values::DateTime.new("#{time1.utc.strftime('%Y%m%dT%H%M%S')}Z"), event2.dtstart assert_equal 'open serie1 + deadline', event2.summary @@ -854,6 +948,7 @@ def with_users_signed_in(users) # Series that are hidden, closed or don't have a deadline should not be present in the ics file event3 = cal.events.third + assert_nil event3 end end diff --git a/test/controllers/evaluation_exercise_controller_test.rb b/test/controllers/evaluation_exercise_controller_test.rb index 35135c57c4..fed31f2751 100644 --- a/test/controllers/evaluation_exercise_controller_test.rb +++ b/test/controllers/evaluation_exercise_controller_test.rb @@ -21,19 +21,21 @@ def setup sign_in user if user.present? evaluation_exercise.update!(visible_score: true) - assert evaluation_exercise.visible_score? + + assert_predicate evaluation_exercise, :visible_score? patch evaluation_exercise_path(evaluation_exercise, format: :js), params: { evaluation_exercise: { visible_score: false } } + assert_response expected evaluation_exercise.reload if expected == :success assert_not evaluation_exercise.visible_score? else - assert evaluation_exercise.visible_score? + assert_predicate evaluation_exercise, :visible_score? end sign_out user if user.present? diff --git a/test/controllers/evaluations_controller_test.rb b/test/controllers/evaluations_controller_test.rb index e7159aae7a..db3b8173c8 100644 --- a/test/controllers/evaluations_controller_test.rb +++ b/test/controllers/evaluations_controller_test.rb @@ -40,6 +40,7 @@ def setup deadline: DateTime.now + 5.minutes } } + assert_response :success assert_nil @series.evaluation end @@ -52,6 +53,7 @@ def setup } } get evaluation_path(@series.evaluation) + assert_redirected_to edit_evaluation_path(@series.evaluation) end @@ -67,6 +69,7 @@ def setup assert_equal @users.count * @exercises.count, @series.evaluation.feedbacks.count post remove_user_evaluation_path(@series.evaluation, user_id: @users.first.id, format: :js) + assert_equal (@users.count - 1) * @exercises.count, @series.evaluation.feedbacks.count end @@ -85,6 +88,7 @@ def setup user.enrolled_courses << @series.course post add_user_evaluation_path(@series.evaluation, user_id: user.id, format: :js) + assert_equal (@users.count + 1) * @exercises.count, @series.evaluation.feedbacks.count end @@ -98,22 +102,26 @@ def setup @series.evaluation.update(users: @series.course.enrolled_members) random_feedback = @series.evaluation.feedbacks.sample + assert_not_nil random_feedback patch evaluation_feedback_path(@series.evaluation, random_feedback), params: { feedback: { completed: true } } random_feedback.reload - assert_equal true, random_feedback.completed, 'completed should have been set to true' + + assert random_feedback.completed, 'completed should have been set to true' patch evaluation_feedback_path(@series.evaluation, random_feedback), params: { feedback: { completed: true } } random_feedback.reload - assert_equal true, random_feedback.completed, 'marking complete should be idempotent' + + assert random_feedback.completed, 'marking complete should be idempotent' patch evaluation_feedback_path(@series.evaluation, random_feedback), params: { feedback: { completed: false } } random_feedback.reload - assert_equal false, random_feedback.completed, 'completed should have been set to false' + + assert_not random_feedback.completed, 'completed should have been set to false' end test 'Notifications should be made when a feedback is released' do @@ -138,11 +146,13 @@ def setup Annotation.create(submission: feedback.submission, annotation_text: Faker::Lorem.sentences(number: 2), line_nr: 0, user: @course_admin) normal_annotations += 1 end + assert_equal normal_annotations, Notification.all.count, 'only notifications for the annotations without a feedback session' evaluation.feedbacks.each do |feedback| feedback.update(completed: true) end + assert_equal normal_annotations, Notification.all.count, 'no new notification should be made upon completing a feedback' evaluation.update(released: true) @@ -171,11 +181,13 @@ def setup end student = @submitted_users.sample + assert_not_nil student picked_submission = evaluation.feedbacks.joins(:evaluation_user).where(evaluation_users: { user: student }).decided.sample.submission get submission_annotations_path(picked_submission, format: :json) json_response = JSON.parse(@response.body) + assert_equal 2, json_response.size, 'Course admin should be able to see unreleased submissions' sign_in student @@ -184,6 +196,7 @@ def setup get submission_annotations_path(picked_submission, format: :json) json_response = JSON.parse(@response.body) + assert_equal 1, json_response.size, 'Only one annotation is visible here, since the feedback session is unreleased' evaluation.update(released: true) @@ -191,6 +204,7 @@ def setup get submission_annotations_path(picked_submission, format: :json) json_response = JSON.parse(@response.body) + assert_equal 2, json_response.size, 'Both annotations are visible, as the feedback session is released' random_unauthorized_student = create :student @@ -199,6 +213,7 @@ def setup get submission_annotations_path(picked_submission, format: :json) json_response = JSON.parse(@response.body) + assert_equal 0, json_response.size, 'Non authorized users can not query for annotations on a submission that is not their own' sign_out random_unauthorized_student @@ -206,6 +221,7 @@ def setup get submission_annotations_path(picked_submission, format: :json) json_response = JSON.parse(@response.body) + assert_equal 0, json_response.size, 'Non logged in users may not query the annotations of a submission' end @@ -228,13 +244,16 @@ def setup # No log in get evaluation_feedback_path(evaluation, random_feedback) + assert_response :redirect # Redirect to sign in page random_user = @users.sample + assert_not random_user.admin_of?(@series.course) sign_in random_user get evaluation_feedback_path(evaluation, random_feedback) + assert_response :redirect # Redirect to sign in page end @@ -248,9 +267,11 @@ def setup evaluation = @series.evaluation evaluation.update(users: @series.course.enrolled_members) feedback = evaluation.feedbacks.where(submission_id: nil).sample + assert_not_nil feedback, 'should have feedback without submission' get evaluation_feedback_path(evaluation, feedback) + assert_response :success end @@ -263,9 +284,11 @@ def setup evaluation_count = Evaluation.where(series: @series).count evaluation = @series.evaluation + assert_not_nil evaluation get new_evaluation_path(series_id: @series.id) + assert_response :redirect post evaluations_path, params: { @@ -274,21 +297,25 @@ def setup users: @users.map(&:id), exercises: @exercises.map(&:id) } } + assert_response :redirect assert_equal evaluation_count, Evaluation.where(series: @series).count, 'No new feedback sessions should be made for this series' sign_out @course_admin get new_evaluation_path(series_id: @series.id) + assert_response :redirect end test 'When there is no previous feedback session for this series, we can query the wizard' do get new_evaluation_path(series_id: @series.id) + assert_response :success sign_out @course_admin get new_evaluation_path(series_id: @series.id) + assert_response :redirect end @@ -305,20 +332,24 @@ def setup @series.course.administrating_members << staff_member get edit_evaluation_path(evaluation) + assert_response :success sign_out @course_admin get edit_evaluation_path(evaluation) + assert_response :redirect sign_in random_student get edit_evaluation_path(evaluation) + assert_response :redirect sign_out random_student assert_not_nil staff_member sign_in staff_member get edit_evaluation_path(evaluation) + assert_response :success end @@ -332,6 +363,7 @@ def setup random_student = create :student student_from_evaluation = @users.sample + assert_not student_from_evaluation.admin_of?(@series.course) evaluation = @series.evaluation staff_member = create :staff @@ -339,6 +371,7 @@ def setup evaluation.feedbacks.decided.each do |feedback| get evaluation_feedback_path(evaluation, feedback) + assert_response :success end sign_out @course_admin @@ -346,6 +379,7 @@ def setup sign_in staff_member evaluation.feedbacks.decided.each do |feedback| get evaluation_feedback_path(evaluation, feedback) + assert_response :success end sign_out staff_member @@ -353,6 +387,7 @@ def setup sign_in random_student evaluation.feedbacks.decided.each do |feedback| get evaluation_feedback_path(evaluation, feedback) + assert_response :redirect end sign_out random_student @@ -360,12 +395,14 @@ def setup sign_in student_from_evaluation evaluation.feedbacks.decided.each do |feedback| get evaluation_feedback_path(evaluation, feedback) + assert_response :redirect end sign_out student_from_evaluation evaluation.feedbacks.decided.each do |feedback| get evaluation_feedback_path(evaluation, feedback) + assert_response :redirect end end @@ -382,6 +419,7 @@ def setup evaluation.update(users: @users) random_student = create :student student_from_evaluation = @users.sample + assert_not student_from_evaluation.admin_of?(@series.course) staff_member = create :staff @series.course.administrating_members << staff_member @@ -389,6 +427,7 @@ def setup [@course_admin, staff_member].each do |person| sign_in person get evaluation_path(evaluation) + assert_response :success, 'Should get access since the user is not a student' sign_out person end @@ -396,6 +435,7 @@ def setup [student_from_evaluation, random_student].each do |person| sign_in person get evaluation_path(evaluation) + assert_response :redirect, 'Should not get access since the user is a student' sign_out person end @@ -418,14 +458,17 @@ def setup score = create :score, score_item: score_item, feedback: feedback get export_grades_evaluation_path evaluation, format: :csv + assert_response :success assert_equal 'text/csv', response.content_type # Check the contents of the csv file. csv = CSV.parse response.body + assert_equal 1 + evaluation.evaluation_users.length, csv.size header = csv.shift + assert_equal 9 + (evaluation.evaluation_exercises.length * 2), header.length # Get which users will have a score @@ -443,12 +486,15 @@ def setup # Only one exercise has a score. if users.key?(line[5]) exported_score = BigDecimal(line.delete_at(score_item_exercise_position)) + assert_equal users[line[5]], exported_score else exported_score = line.delete_at(score_item_exercise_position) + assert_equal '', exported_score end exported_max = BigDecimal(line.delete_at(score_item_exercise_position)) + assert_equal score_item.maximum, exported_max # All other scores should be nil. @@ -459,13 +505,16 @@ def setup # No log in get export_grades_evaluation_path evaluation, format: :csv + assert_response :redirect # Redirect to sign in page random_user = @users.sample + assert_not random_user.admin_of?(@series.course) sign_in random_user get export_grades_evaluation_path evaluation, format: :csv + assert_response :redirect # Redirect to sign in page end @@ -492,6 +541,7 @@ def setup create :score, score_item: score_item2, feedback: feedback2, score: 7.5 get export_grades_evaluation_path evaluation, format: :csv + assert_response :success assert_equal 'text/csv', response.content_type @@ -505,7 +555,7 @@ def setup total_maximum = 0 if line[7] == '' (9...line.length - 1).step(2).each do |index| - assert_equal line[index], '' + assert_equal('', line[index]) total_maximum += BigDecimal(line[index + 1]) unless line[index + 1] == '' end else @@ -518,6 +568,7 @@ def setup assert_equal BigDecimal(line[7]), total_score end + assert_equal BigDecimal(line[8]), total_maximum end end @@ -552,9 +603,9 @@ def setup s2.reload if expected == :redirect - assert from.visible_score? - assert s1.visible? - assert s2.visible? + assert_predicate from, :visible_score? + assert_predicate s1, :visible? + assert_predicate s2, :visible? else assert_not from.visible_score? assert_not s1.visible? @@ -573,10 +624,12 @@ def setup assert_not evaluation.released sign_in submission.user get overview_evaluation_path(evaluation) + assert_response :redirect # Redirect to sign in page evaluation.update!(released: true) get overview_evaluation_path(evaluation) + assert_response :ok end @@ -602,6 +655,7 @@ def expected_score_string(*args) # Visible scores are visible get overview_evaluation_path(evaluation) + assert_match visible_score_item.description, response.body assert_no_match hidden_score_item.description, response.body assert_match expected_score_string(s1), response.body @@ -611,6 +665,7 @@ def expected_score_string(*args) # Hidden total is not shown evaluation_exercise.update!(visible_score: false) get overview_evaluation_path(evaluation) + assert_match visible_score_item.description, response.body assert_no_match hidden_score_item.description, response.body assert_match expected_score_string(s1), response.body diff --git a/test/controllers/exports_controller_test.rb b/test/controllers/exports_controller_test.rb index 050f0bc478..ff8c4932a6 100644 --- a/test/controllers/exports_controller_test.rb +++ b/test/controllers/exports_controller_test.rb @@ -20,13 +20,16 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest test 'should retrieve download solutions wizard page' do get series_exports_path(@series) + assert_response :success end test 'should download only last submissions' do post series_exports_path(@series), params: { all: true, only_last_submission: true, with_info: true } + assert_redirected_to exports_path count = @students.map { |u| @series.exercises.map { |e| e.last_submission(u, @series) } }.flatten.select(&:present?).count + assert_zip ActiveStorage::Blob.last.download, with_info: true, solution_count: count, data: @data end @@ -39,24 +42,27 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest test 'should be grouped by user' do post series_exports_path(@series), params: { all: true, group_by: 'user' } + assert_redirected_to exports_path assert_zip ActiveStorage::Blob.last.download, group_by: 'user', data: @data end test 'should be grouped by exercise' do post series_exports_path(@series), params: { all: true, group_by: 'exercise' } + assert_redirected_to exports_path assert_zip ActiveStorage::Blob.last.download, group_by: 'exercise', data: @data end test 'should retrieve all submissions' do post series_exports_path(@series), params: { all: true } + assert_redirected_to exports_path assert_zip ActiveStorage::Blob.last.download, solution_count: Submission.all.count, data: @data end test 'all students should be present in the zip' do - @new_student = create(:student) + @new_student = create :student @course.enrolled_members.push(@new_student) @data[:users].push(@new_student) zip_submission_count = @data[:users].map do |u| @@ -68,6 +74,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest end.flatten.length post series_exports_path(@series), params: { all: true, all_students: true } + assert_redirected_to exports_path assert_zip ActiveStorage::Blob.last.download, solution_count: zip_submission_count, data: @data end @@ -75,14 +82,18 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest test 'zip should only contain submissions before deadline' do @series.update(deadline: 1.year.ago) post series_exports_path(@series), params: { all: true, deadline: true } + assert_redirected_to exports_path zip_submission_count = @series.exercises.map { |ex| ex.submissions.before_deadline(@series.deadline) }.flatten.length + assert_zip ActiveStorage::Blob.last.download, solution_count: zip_submission_count, data: @data @series.update(deadline: 2.years.from_now) post series_exports_path(@series), params: { all: true, deadline: true } + assert_redirected_to exports_path zip_submission_count = @series.exercises.map { |ex| ex.submissions.before_deadline(@series.deadline) }.flatten.length + assert_zip ActiveStorage::Blob.last.download, solution_count: zip_submission_count, data: @data end @@ -96,6 +107,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest subs end end.flatten.length + assert_zip ActiveStorage::Blob.last.download, solution_count: zip_submission_count, data: @data end @@ -116,6 +128,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest end end.flatten.length post series_exports_path(@series), params: options + assert_zip ActiveStorage::Blob.last.download, options end @@ -128,8 +141,10 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest all: true } post courses_exports_path(@course), params: options + assert_redirected_to exports_path options[:group_by] = 'series' + assert_zip ActiveStorage::Blob.last.download, options end @@ -146,8 +161,10 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest all: true } post courses_exports_path(@course), params: options + assert_redirected_to exports_path options[:group_by] = 'series' + assert_zip ActiveStorage::Blob.last.download, options end @@ -167,8 +184,10 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest } sign_in u post courses_exports_path(@course), params: options + assert_redirected_to exports_path options[:group_by] = 'series' + assert_zip ActiveStorage::Blob.last.download, options end @@ -184,6 +203,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest solution_count: @course.users.count * @course.series.map(&:exercises).flatten.count } post courses_exports_path(@course), params: options + assert_redirected_to exports_path assert_zip ActiveStorage::Blob.last.download, options end @@ -204,6 +224,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest end.sum post courses_exports_path(@course), params: options options[:group_by] = 'series' + assert_zip ActiveStorage::Blob.last.download, options end @@ -217,6 +238,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest @data[:user] = student post users_exports_path(student), params: options options[:group_by] = 'course' + assert_zip ActiveStorage::Blob.last.download, options end @@ -229,10 +251,12 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest solution_count: Submission.all.of_user(other_student).count } post users_exports_path(other_student, format: :json), params: options + assert_response :forbidden sign_in create(:staff, administrating_courses: [@course]) post users_exports_path(other_student, format: :json), params: options + assert_response :forbidden end @@ -240,6 +264,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest sign_in @students[0] post courses_exports_path(@course, user_id: @students[0].id, format: :json) + assert_response :accepted end @@ -248,6 +273,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest sign_in u post courses_exports_path(@course, user_id: u.id, format: :json) + assert_response :forbidden end @@ -255,6 +281,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest sign_in @students[0] post courses_exports_path(@course, format: :json) + assert_response :forbidden end @@ -262,6 +289,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest sign_in @students[0] post series_exports_path(@course.series.first, user_id: @students[0].id, format: :json) + assert_response :accepted end @@ -270,6 +298,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest sign_in u post series_exports_path(@course.series.first, user_id: u.id, format: :json) + assert_response :forbidden end @@ -277,6 +306,7 @@ class ExportsControllerTest < ActionDispatch::IntegrationTest sign_in @students[0] post series_exports_path(@course.series.first, format: :json) + assert_response :forbidden end end diff --git a/test/controllers/feedbacks_controller_test.rb b/test/controllers/feedbacks_controller_test.rb index 492531b58c..302970b372 100644 --- a/test/controllers/feedbacks_controller_test.rb +++ b/test/controllers/feedbacks_controller_test.rb @@ -44,6 +44,7 @@ def setup test 'Scores are reset when a submission is changed' do create :score, feedback: @feedback, score_item: @score_item1 @feedback.reload + assert_equal 1, @feedback.scores.count s = create :submission, exercise: @feedback.exercise, user: @feedback.user, course: @feedback.evaluation.series.course @@ -54,12 +55,14 @@ def setup } } @feedback.reload + assert_equal s.id, @feedback.submission_id assert_equal 0, @feedback.scores.count end test 'scores are reset when a submission is changed from empty' do feedback = @evaluation.feedbacks.find_by(submission_id: nil, evaluation_exercise: @evaluation.evaluation_exercises.first) + assert_not_empty feedback.scores s = create :submission, exercise: feedback.exercise, user: feedback.user, course: feedback.evaluation.series.course @@ -70,6 +73,7 @@ def setup } } feedback.reload + assert_equal s.id, feedback.submission_id assert_empty feedback.scores end @@ -84,6 +88,7 @@ def setup create :score, feedback: @feedback, score_item: si end @feedback.reload + assert_equal 10, @feedback.scores.count s = create :submission, exercise: @feedback.exercise, user: @feedback.user, course: @feedback.evaluation.series.course @@ -94,6 +99,7 @@ def setup } } @feedback.reload + assert_equal s.id, @feedback.submission_id assert_equal 0, @feedback.scores.count end @@ -101,12 +107,13 @@ def setup test 'deleting all the scores of an evaluation works' do create :score, feedback: @feedback, score_item: @score_item1 @feedback.reload + assert_equal 1, @feedback.scores.count delete scores_feedback_path(@feedback) @feedback.reload - assert @feedback.scores.empty? + assert_empty @feedback.scores end end diff --git a/test/controllers/institution_controller_test.rb b/test/controllers/institution_controller_test.rb index a4a8c61b1a..65721e2a8e 100644 --- a/test/controllers/institution_controller_test.rb +++ b/test/controllers/institution_controller_test.rb @@ -26,22 +26,26 @@ class InstitutionControllerTest < ActionDispatch::IntegrationTest create :provider, institution: @instance, mode: :prefer p2 = create :provider, institution: @instance, mode: :secondary put institution_path(@instance, { institution: { providers_attributes: [{ id: p2.id, mode: :redirect }] } }) + assert_redirected_to institutions_path - assert p2.reload.redirect? + assert_predicate p2.reload, :redirect? end test 'should not be able to invalidly edit provider mode' do create :provider, institution: @instance, mode: :prefer p2 = create :provider, institution: @instance, mode: :secondary put institution_path(@instance, { institution: { providers_attributes: [{ id: p2.id, mode: :prefer }] } }) + assert_response :unprocessable_entity - assert p2.reload.secondary? + assert_predicate p2.reload, :secondary? end test 'should render merge page' do get merge_institution_path(@instance) + assert_response :success get merge_institution_path(@instance, format: :js), xhr: true + assert_response :success end @@ -49,12 +53,14 @@ class InstitutionControllerTest < ActionDispatch::IntegrationTest other = create :institution create :provider, institution: @instance, mode: :prefer get merge_changes_institution_path(@instance, other_institution_id: other.id, format: :js), xhr: true + assert_response :success end test 'should do merge' do other = create :institution post merge_institution_path(@instance, other_institution_id: other.id) + assert_redirected_to institution_path(other) assert_not Institution.exists?(id: @instance.id) end @@ -65,6 +71,7 @@ class InstitutionControllerTest < ActionDispatch::IntegrationTest institution = create :institution create :user, institution: institution, username: user.username post merge_institution_path(@instance, other_institution_id: institution.id) + assert_response :unprocessable_entity end end diff --git a/test/controllers/lti_controller_test.rb b/test/controllers/lti_controller_test.rb index 6829bdb5ec..9cda2d1bc3 100644 --- a/test/controllers/lti_controller_test.rb +++ b/test/controllers/lti_controller_test.rb @@ -14,11 +14,11 @@ class LtiControllerTest < ActionDispatch::IntegrationTest def setup super - @provider = create(:lti_provider) + @provider = create :lti_provider end test 'content selection shows courses' do - courses = create_list(:course, 2) + courses = create_list :course, 2 payload = lti_payload('nonce', 'target', 'LtiDeepLinkingRequest') id_token = encode_jwt(payload) @@ -53,11 +53,13 @@ def setup assert_response :ok encoded = JSON.parse(@response.body)['payload'] decoded = decode_jwt(encoded) + assert_equal @provider.client_id, decoded['iss'] assert_equal @provider.issuer, decoded['aud'] assert_equal 'nonce', decoded['nonce'] assert_not_empty decoded['https://purl.imsglobal.org/spec/lti-dl/claim/data'] items = decoded['https://purl.imsglobal.org/spec/lti-dl/claim/content_items'] + assert_equal [{ 'type' => 'ltiResourceLink', 'title' => series.exercises.first.name, @@ -78,6 +80,7 @@ def get_jwks_content(_uri) end get course_path course, id_token: id_token, provider_id: @provider.id + assert_response :ok assert_not_empty flash[:error] @@ -96,7 +99,7 @@ class LtiFlowTest < ActionDispatch::IntegrationTest def setup super - @provider = create(:lti_provider) + @provider = create :lti_provider OmniAuth.config.test_mode = false end @@ -118,9 +121,11 @@ def teardown # Described by section 5.1.1.2 of the IMS Security Framework. assert_response :found location = URI.parse(@response.header['Location']) + assert_equal @provider.authorization_uri, "#{location.scheme}://#{location.host}#{location.path}" params = URI.decode_www_form(location.query).to_h.symbolize_keys - assert params[:scope].include? 'openid' + + assert_includes params[:scope], 'openid' assert_equal 'id_token', params[:response_type] assert_equal @provider.client_id, params[:client_id] assert_equal 'https://www.example.com/users/auth/lti/callback', params[:redirect_uri] @@ -145,6 +150,7 @@ def teardown assert_response :found target_uri = URI.parse(@response.header['Location']) params = URI.decode_www_form(target_uri.query).to_h.symbolize_keys + assert_equal target, "#{target_uri.scheme}://#{target_uri.host}#{target_uri.path}" assert_equal @provider.id.to_s, params[:provider_id] assert_not_empty params[:id_token] @@ -174,6 +180,7 @@ def teardown assert_response :redirect target_uri = URI.parse(@response.header['Location']) params = URI.decode_www_form(target_uri.query).to_h.symbolize_keys + assert_equal content_selection_path, target_uri.path assert_equal @provider.id.to_s, params[:provider_id] assert_not_empty params[:id_token] diff --git a/test/controllers/notifications_controller_test.rb b/test/controllers/notifications_controller_test.rb index 626499a271..447a9f9278 100644 --- a/test/controllers/notifications_controller_test.rb +++ b/test/controllers/notifications_controller_test.rb @@ -9,16 +9,19 @@ class NotificationsControllerTest < ActionDispatch::IntegrationTest test 'should get index' do get notifications_url(format: :json) + assert_response :success end test 'should render index' do get notifications_url + assert_response :success end test 'should update notification' do patch notification_url(@notification, format: :json), params: { notification: { read: true } } + assert_response :success end diff --git a/test/controllers/pages_controller_test.rb b/test/controllers/pages_controller_test.rb index 46e16089d4..e8c98d7902 100644 --- a/test/controllers/pages_controller_test.rb +++ b/test/controllers/pages_controller_test.rb @@ -5,17 +5,20 @@ class PagesControllerTest < ActionDispatch::IntegrationTest test 'should get homepage' do get root_url + assert_response :success end test 'should get homepage as json' do get root_url(format: :json) + assert_response :success end test 'should get signed in homepage' do sign_in(users(:student)) get root_url + assert_response :success end @@ -29,11 +32,13 @@ class PagesControllerTest < ActionDispatch::IntegrationTest sign_in(user) get root_url + assert_response :success end test 'should get contact page' do get contact_url + assert_response :success end @@ -67,23 +72,27 @@ class PagesControllerTest < ActionDispatch::IntegrationTest user = users(:student) sign_in user get profile_url + assert_response :redirect assert_redirected_to user_path(user) end test 'should not get profile when logged out' do get profile_url + assert_response :redirect assert_redirected_to sign_in_url end test 'should get support us page' do get support_us_url + assert_response :success end test 'should get about page' do get about_url + assert_response :success end end diff --git a/test/controllers/repositories_controller_test.rb b/test/controllers/repositories_controller_test.rb index aa89ecf837..8c80b40ba1 100644 --- a/test/controllers/repositories_controller_test.rb +++ b/test/controllers/repositories_controller_test.rb @@ -14,7 +14,7 @@ class RepositoriesControllerTest < ActionDispatch::IntegrationTest sign_in @admin end - def request_public_image + def test_request_public_image get public_repository_url(@instance, 'CodersApprentice.png'), headers: { range: 'bytes=150-300' } assert_response :success @@ -28,22 +28,24 @@ def request_public_image test 'should reprocess activities' do Repository.any_instance.expects(:process_activities) get reprocess_repository_path(@instance) + assert_redirected_to(@instance) end test 'should reprocess activities on judge change' do Repository.any_instance.expects(:process_activities) patch repository_path(@instance), params: { repository: { judge_id: create(:judge, :git_stubbed).id } } + assert_redirected_to(@instance) end test 'should get public media' do - request_public_image + test_request_public_image end test 'public media should be public' do sign_out @admin - request_public_image + test_request_public_image end test 'should create repository admin on create' do @@ -100,17 +102,20 @@ def request_public_image @instance.admins << @admin post remove_admin_repository_url(@instance, user_id: @admin.id) - assert @instance.admins.include? @admin + + assert_includes @instance.admins, @admin end test 'allowed courses should render' do course = courses(:course1) @instance.allowed_courses << course get courses_repository_url(@instance) + assert_response :success user = users(:student) @instance.admins << user get courses_repository_url(@instance) + assert_response :success end @@ -159,8 +164,10 @@ def request_public_image test 'only zeus should be able to edit featured' do f = !@instance.featured patch repository_path(@instance), params: { repository: { featured: f }, format: :json } + assert_response :success @instance.reload + assert_equal f, @instance.featured sign_out @admin @@ -171,6 +178,7 @@ def request_public_image f = !@instance.featured patch repository_path(@instance), params: { repository: { featured: f }, format: :json } @instance.reload + assert_not_equal f, @instance.featured sign_out user @@ -205,6 +213,7 @@ def find_echo test 'webhook without commit info should update exercises' do post webhook_repository_path(@repository) + assert_equal 'private', find_echo.access end @@ -244,6 +253,7 @@ def find_echo modified: ['echo/config.json'] }] post webhook_repository_path(@repository), params: { commits: commit_info }, headers: { 'X-GitHub-Event': 'push' } + assert_equal 'private', find_echo.access end @@ -260,6 +270,7 @@ def find_echo modified: ['echo/config.json'] }] post webhook_repository_path(@repository), params: { commits: commit_info }, headers: { 'X-Gitlab-Event': 'push' } + assert_equal 'private', find_echo.access end @@ -288,6 +299,7 @@ def find_echo post webhook_repository_path(@repository), params: params, headers: { 'X-GitHub-Event': 'push' } email = ActionMailer::Base.deliveries.last + assert_equal ['a@ugent.be'], email.to end @@ -308,6 +320,7 @@ def find_echo post webhook_repository_path(@repository), params: params, headers: { 'X-Gitlab-Event': 'push' } email = ActionMailer::Base.deliveries.last + assert_equal ['a@ugent.be'], email.to end @@ -337,6 +350,7 @@ def find_echo post webhook_repository_path(@repository), params: params, headers: { 'X-GitHub-Event': 'push' } email = ActionMailer::Base.deliveries.last + assert_equal [user.email], email.to end @@ -367,6 +381,7 @@ def find_echo post webhook_repository_path(@repository), params: params, headers: { 'X-GitHub-Event': 'push' } email = ActionMailer::Base.deliveries.last + assert_equal [user.email], email.to end @@ -393,6 +408,7 @@ def find_echo post webhook_repository_path(@repository), params: params, headers: { 'X-Gitlab-Event': 'push' } email = ActionMailer::Base.deliveries.last + assert_equal [user.email], email.to end @@ -420,6 +436,7 @@ def find_echo post webhook_repository_path(@repository), params: params, headers: { 'X-Gitlab-Event': 'push' } email = ActionMailer::Base.deliveries.last + assert_equal [user.email], email.to end end diff --git a/test/controllers/rights_requests_controller_test.rb b/test/controllers/rights_requests_controller_test.rb index 3513e13ab5..89b6eb04f7 100644 --- a/test/controllers/rights_requests_controller_test.rb +++ b/test/controllers/rights_requests_controller_test.rb @@ -15,6 +15,7 @@ class RightsRequestsControllerTest < ActionDispatch::IntegrationTest assert_difference 'ActionMailer::Base.deliveries.size', 1 do attrs = generate_attr_hash create_request(attr_hash: attrs) + assert_redirected_to root_path end end @@ -23,40 +24,44 @@ class RightsRequestsControllerTest < ActionDispatch::IntegrationTest attrs = generate_attr_hash attrs.delete(:context) create_request(attr_hash: attrs) + assert_response :unprocessable_entity end test 'zeus should be able to get index' do - create(:rights_request) + create :rights_request sign_in users(:zeus) get rights_requests_url + assert_response :success end test 'others should not be able to get index' do - create(:rights_request) + create :rights_request get rights_requests_url + assert_redirected_to root_path end test 'zeus should be able to approve' do sign_in users(:zeus) - req = create(:rights_request) + req = create :rights_request assert_difference 'ActionMailer::Base.deliveries.size', 1 do post approve_rights_request_url(req, format: :js) end assert_response :success - assert req.user.reload.staff? + assert_predicate req.user.reload, :staff? - req = create(:rights_request) + req = create :rights_request post approve_rights_request_url(req) + assert_redirected_to rights_requests_path - assert req.user.reload.staff? + assert_predicate req.user.reload, :staff? end test 'approval should update institution name' do sign_in create(:zeus, :with_institution) - req = create(:rights_request) + req = create :rights_request req.update(institution_name: "#{req.user.institution.name}-different") assert_difference 'ActionMailer::Base.deliveries.size', 1 do post approve_rights_request_url(req, format: :js) @@ -66,7 +71,7 @@ class RightsRequestsControllerTest < ActionDispatch::IntegrationTest end test 'others should not be able to approve' do - req = create(:rights_request) + req = create :rights_request assert_difference 'ActionMailer::Base.deliveries.size', 0 do post approve_rights_request_url(req) end @@ -75,20 +80,22 @@ class RightsRequestsControllerTest < ActionDispatch::IntegrationTest test 'zeus should be able to reject' do sign_in users(:zeus) - req = create(:rights_request) + req = create :rights_request assert_difference 'ActionMailer::Base.deliveries.size', 1 do post reject_rights_request_url(req, format: :js) end assert_response :success - req = create(:rights_request) + req = create :rights_request post reject_rights_request_url(req) + assert_redirected_to rights_requests_path end test 'others should not be able to reject' do - req = create(:rights_request) + req = create :rights_request post reject_rights_request_url(req) + assert_redirected_to root_path end end diff --git a/test/controllers/saved_annotation_controller_test.rb b/test/controllers/saved_annotation_controller_test.rb index 35d89c8974..428346964a 100644 --- a/test/controllers/saved_annotation_controller_test.rb +++ b/test/controllers/saved_annotation_controller_test.rb @@ -18,14 +18,17 @@ def setup test 'should be able to create from existing annotation' do annotation = create :annotation, submission: create(:submission, course: @course), user: @user post saved_annotations_url, params: { format: :json, saved_annotation: { title: 'test', annotation_text: annotation.annotation_text }, from: annotation.id } + assert_response :success end test 'creating a saved annotation should fail when one with the same name already exists' do annotation = create :annotation, submission: create(:submission, course: @course), user: @user post saved_annotations_url, params: { format: :json, saved_annotation: { title: 'test', annotation_text: annotation.annotation_text }, from: annotation.id } + assert_response :success post saved_annotations_url, params: { format: :json, saved_annotation: { title: 'test', annotation_text: annotation.annotation_text }, from: annotation.id } + assert_response :unprocessable_entity end end diff --git a/test/controllers/score_items_controller_test.rb b/test/controllers/score_items_controller_test.rb index 6bd1d70964..781fcc97e7 100644 --- a/test/controllers/score_items_controller_test.rb +++ b/test/controllers/score_items_controller_test.rb @@ -28,6 +28,7 @@ def setup to: to.id } } + assert_response expected assert_equal 2, to.score_items.count if expected == :success @@ -57,6 +58,7 @@ def setup assert_equal 1, e.score_items.length e.update!(score_items: []) end + assert_empty e.score_items end sign_out user if user.present? @@ -83,6 +85,7 @@ def setup maximum: '20.0' } } + assert_response expected sign_out user if user.present? end @@ -105,6 +108,7 @@ def setup evaluation_exercise_id: @evaluation.evaluation_exercises.first.id } } + assert_response expected sign_out user if user.present? end @@ -118,6 +122,7 @@ def setup evaluation_exercise_id: @evaluation.evaluation_exercises.first.id } } + assert_response :unprocessable_entity # Negative maximum @@ -128,6 +133,7 @@ def setup evaluation_exercise_id: @evaluation.evaluation_exercises.first.id } } + assert_response :unprocessable_entity end @@ -139,6 +145,7 @@ def setup maximum: '-20.0' } } + assert_response :unprocessable_entity end @@ -157,9 +164,11 @@ def setup score_item = create :score_item, evaluation_exercise: exercise, description: 'Code re-use', maximum: '10.0' + assert_equal 1, exercise.score_items.count sign_in user if user.present? delete evaluation_score_item_path(@evaluation, score_item, format: :json) + assert_response expected exercise.score_items.reload assert_equal 0, exercise.score_items.count if response == :success diff --git a/test/controllers/scores_controller_test.rb b/test/controllers/scores_controller_test.rb index 9c32a2a4c8..592cd23de8 100644 --- a/test/controllers/scores_controller_test.rb +++ b/test/controllers/scores_controller_test.rb @@ -30,6 +30,7 @@ def setup feedback_id: @feedback.id } } + assert_response expected sign_out user if user.present? Score.where(score_item: @score_item, feedback: @feedback).first.destroy! if expected == :created @@ -43,6 +44,7 @@ def setup feedback_id: @feedback.id } } + assert_response :unprocessable_entity end @@ -62,6 +64,7 @@ def setup expected_score: score.score.to_s } } + assert_response expected sign_out user if user.present? end @@ -75,6 +78,7 @@ def setup expected_score: score.score.to_s } } + assert_response :unprocessable_entity end @@ -87,6 +91,7 @@ def setup expected_score: '11.0' } } + assert_response :forbidden end @@ -105,6 +110,7 @@ def setup expected_score: score.score.to_s } } + assert_response expected sign_out user if user.present? score.destroy! @@ -119,6 +125,7 @@ def setup expected_score: '11.0' } } + assert_response :forbidden end @@ -135,6 +142,7 @@ def setup assert_response :unprocessable_entity score = Score.find_by!(score_item: @score_item, feedback: @feedback) + assert_equal BigDecimal('10'), score.score end end diff --git a/test/controllers/series_controller_test.rb b/test/controllers/series_controller_test.rb index 9ccfd9f73e..b4aaf994b4 100644 --- a/test/controllers/series_controller_test.rb +++ b/test/controllers/series_controller_test.rb @@ -6,7 +6,7 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest crud_helpers Series, attrs: %i[name description course_id visibility order deadline] setup do - @instance = create(:series) + @instance = create :series sign_in users(:zeus) end @@ -16,17 +16,20 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest exercise = create :exercise, programming_language: nil @instance.exercises << exercise get series_url(@instance) + assert_response :success end test 'should get new for course' do course = courses(:course1) get new_course_series_url(course) + assert_response :success end test 'create series should redirect to edit' do instance = create_request_expect + assert_redirected_to edit_series_url(instance) end @@ -36,9 +39,11 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest course = courses(:course1) get new_course_series_url(course) + assert_response :redirect create_request + assert_response :redirect end @@ -62,30 +67,35 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest test 'create series with missing course_id should 422' do post series_index_url, params: { series: { name: 'Test series' }, format: :json } + assert_response :unprocessable_entity end test 'update series should redirect to course' do instance = update_request_expect(attr_hash: { series: { description: 'new description' } }) + assert_redirected_to course_url(instance.course, series: instance, anchor: instance.anchor) end test 'destroy series should redirect to course' do course = @instance.course destroy_request + assert_redirected_to course_url(course) end test 'should generate scoresheet' do - series = create(:series, :with_submissions) + series = create :series, :with_submissions get scoresheet_series_path(series) + assert_response :success end test 'should mass rejudge' do - series = create(:series, :with_submissions) + series = create :series, :with_submissions assert_jobs_enqueued(Submission.in_series(series).count) do post mass_rejudge_series_path(series), params: { format: 'application/javascript' } + assert_response :success end end @@ -98,17 +108,19 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest format: 'application/javascript', activity_id: activity.id } + assert_response :success - assert @instance.reload.activities.include? activity + assert_includes @instance.reload.activities, activity end test 'should remove activity from series' do - activity = create(:exercise, series: [@instance]) + activity = create :exercise, series: [@instance] post remove_activity_series_path(@instance), params: { format: 'application/javascript', activity_id: activity.id } + assert_response :success assert_not @instance.activities.include?(activity) end @@ -116,7 +128,8 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest test 'repository admin adding private activity to series should add course to repository\'s allowed courses' do activity = create :exercise, access: :private post add_activity_series_path @instance, params: { format: 'application/javascript', activity_id: activity.id } - assert activity.repository.allowed_courses.include? @instance.course + + assert_includes activity.repository.allowed_courses, @instance.course end test 'course admin should not be able to add private activity to series' do @@ -125,14 +138,16 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest sign_in user @instance.course.administrating_members << user post add_activity_series_path @instance, params: { format: 'application/javascript', activity_id: activity.id } + assert_not @instance.activities.include? activity end test 'should reorder activities' do - activities = create_list(:exercise, 10, series: [@instance]) + activities = create_list :exercise, 10, series: [@instance] activities.shuffle! ids = activities.map(&:id) post reorder_activities_series_path @instance, params: { format: 'application/javascript', order: ids.to_json } + assert_response :success assert_equal ids, @instance.series_memberships.map(&:activity_id) end @@ -152,6 +167,7 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest create :exercise, series: [series] get series_url(series) + assert_response :success assert_match(/deadline-future/, response.body) end @@ -172,6 +188,7 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest patch series_url(@instance, format: :json, series: { description: updated_description }), params: { format: :json }, headers: { 'Authorization' => token.token } + assert_response :success assert_equal updated_description, @instance.reload.description ensure @@ -193,6 +210,7 @@ class SeriesControllerTest < ActionDispatch::IntegrationTest ActionController::Base.allow_forgery_protection = true patch series_url(@instance, format: :json, series: { description: updated_description }), params: { format: :json } + assert_response :unauthorized assert_not_equal updated_description, @instance.reload.description ensure @@ -214,8 +232,10 @@ class SeriesVisibilityTest < ActionDispatch::IntegrationTest def assert_show_and_overview(authorized, token: nil) response = authorized ? :success : :redirect get series_url(@series, token: token) + assert_response response get overview_series_url(@series, token: token) + assert_response response end @@ -251,79 +271,95 @@ def assert_show_and_overview(authorized, token: nil) test 'student should see visible series' do sign_in @student + assert_show_and_overview true end test 'student should not see hidden or closed series without token' do sign_in @student @series.update(visibility: :hidden) + assert_show_and_overview false @series.update(visibility: :closed) + assert_show_and_overview false end test 'student should see hidden series with token' do sign_in @student @series.update(visibility: :hidden) + assert_show_and_overview true, token: @series.access_token end test 'student should not see hidden series with wrong token' do sign_in @student @series.update(visibility: :hidden) + assert_show_and_overview false, token: 'hunter2' end test 'student should not see closed series with token' do sign_in @student @series.update(visibility: :closed) + assert_show_and_overview false, token: @series.access_token end test 'not logged in should not see hidden or closed series' do @series.update(visibility: :hidden) + assert_show_and_overview false @series.update(visibility: :closed) + assert_show_and_overview false end test 'should get series scoresheet as course admin' do sign_in @course_admin get scoresheet_series_url(@series) + assert_response :success, "#{@course_admin} should be able to get series scoresheet" end test 'should get series scoresheet in csv format as course admin' do sign_in @course_admin get scoresheet_series_url(@series, format: :csv) + assert_response :success, "#{@course_admin} should be able to get series scoresheet" end test 'should get series scoresheet in json format as course admin' do sign_in @course_admin get scoresheet_series_url(@series, format: :json) + assert_response :success, "#{@course_admin} should be able to get series scoresheet" end test 'should not get series scoresheet as normal user' do sign_in @student get scoresheet_series_url(@series) + assert_response :redirect, "#{@student} should not be able to get series scoresheet" end test 'course admin should see hidden and closed series without token' do sign_in @course_admin @series.update(visibility: :hidden) + assert_show_and_overview true @series.update(visibility: :closed) + assert_show_and_overview true end test 'zeus should see hidden and closed series without token' do sign_in @zeus @series.update(visibility: :hidden) + assert_show_and_overview true @series.update(visibility: :closed) + assert_show_and_overview true end end diff --git a/test/controllers/statistics_controller_test.rb b/test/controllers/statistics_controller_test.rb index 7e797a5788..2425518df0 100644 --- a/test/controllers/statistics_controller_test.rb +++ b/test/controllers/statistics_controller_test.rb @@ -17,21 +17,25 @@ def setup # violin get violin_path format: :json, params: { series_id: series.id } results = response.parsed_body + assert_equal 2, results['data'].count # stacked get stacked_status_path format: :json, params: { series_id: series.id } results = response.parsed_body + assert_equal 2, results['data'].count # time series get timeseries_path format: :json, params: { series_id: series.id } results = response.parsed_body + assert_equal 2, results['data'].count # ctimeseries get cumulative_timeseries_path format: :json, params: { series_id: series.id } results = response.parsed_body + assert_equal 2, results['data'].count end end diff --git a/test/controllers/submissions_controller_test.rb b/test/controllers/submissions_controller_test.rb index 8d4fdeacc1..f6b9a3de63 100644 --- a/test/controllers/submissions_controller_test.rb +++ b/test/controllers/submissions_controller_test.rb @@ -105,6 +105,7 @@ class SubmissionsControllerTest < ActionDispatch::IntegrationTest test 'submission http caching works' do get submissions_path + assert_response :ok assert_not_empty @response.headers['ETag'] assert_not_empty @response.headers['Last-Modified'] @@ -112,6 +113,7 @@ class SubmissionsControllerTest < ActionDispatch::IntegrationTest 'If-None-Match' => @response.headers['ETag'], 'If-Modified-Since' => @response.headers['Last-Modified'] } + assert_response :not_modified end @@ -120,11 +122,12 @@ class SubmissionsControllerTest < ActionDispatch::IntegrationTest assert_jobs_enqueued(1) do submission = create_request_expect end - assert submission.queued? + assert_predicate submission, :queued? end test 'create submission should respond with ok' do create_request_expect + assert_response :success end @@ -132,6 +135,7 @@ class SubmissionsControllerTest < ActionDispatch::IntegrationTest attrs = generate_attr_hash attrs[:exercise_id] = create(:content_page).id create_request(attr_hash: attrs) + assert_response :unprocessable_entity end @@ -139,11 +143,13 @@ class SubmissionsControllerTest < ActionDispatch::IntegrationTest attrs = generate_attr_hash attrs.delete(:exercise_id) create_request(attr_hash: attrs) + assert_response :unprocessable_entity end test 'create submission should respond bad_request without a hash' do post submissions_url + assert_response :bad_request end @@ -194,6 +200,7 @@ class SubmissionsControllerTest < ActionDispatch::IntegrationTest test 'should get submission edit page' do get edit_submission_path(@instance) + assert_redirected_to activity_url( @instance.exercise, anchor: 'submission-card', @@ -203,33 +210,38 @@ class SubmissionsControllerTest < ActionDispatch::IntegrationTest test 'should download submission code' do get download_submission_path(@instance) + assert_response :success end test 'should evaluate submission' do assert_difference('Delayed::Job.count', +1) do get evaluate_submission_path(@instance) + assert_redirected_to @instance end end test 'submission media should redirect to exercise media' do get media_submission_path(@instance, 'dank_meme.jpg') + assert_redirected_to media_activity_path(@instance.exercise, 'dank_meme.jpg') end test 'submission media should redirect to exercise media and keep token' do get media_submission_path(@instance, 'dank_meme.jpg', token: @instance.exercise.access_token) + assert_redirected_to media_activity_path(@instance.exercise, 'dank_meme.jpg', token: @instance.exercise.access_token) end def rejudge_submissions(**params) post mass_rejudge_submissions_path, params: params + assert_response :success end test 'should enqeueue submissions delayed ' do - create(:series, :with_submissions) + create :series, :with_submissions # in test env, default and export queues are evaluated inline with_delayed_jobs do @@ -241,14 +253,14 @@ def rejudge_submissions(**params) end test 'should rejudge all submissions' do - create(:series, :with_submissions) + create :series, :with_submissions assert_jobs_enqueued(Submission.count) do rejudge_submissions end end test 'should rejudge user submissions' do - series = create(:series, :with_submissions) + series = create :series, :with_submissions user = User.in_course(series.course).sample assert_jobs_enqueued(user.submissions.count) do rejudge_submissions user_id: user.id @@ -256,7 +268,7 @@ def rejudge_submissions(**params) end test 'should rejudge course submissions' do - series = create(:series, :with_submissions) + series = create :series, :with_submissions series.course.subscribed_members << @zeus assert_jobs_enqueued(Submission.in_course(series.course).count) do rejudge_submissions course_id: series.course.id @@ -264,7 +276,7 @@ def rejudge_submissions(**params) end test 'should rejudge series submissions' do - series = create(:series, :with_submissions) + series = create :series, :with_submissions series.course.subscribed_members << @zeus assert_jobs_enqueued(Submission.in_series(series).count) do rejudge_submissions series_id: series.id @@ -300,6 +312,7 @@ def expected_score_string(*args) sign_in submission.user get submission_url(id: submission.id) + assert_match visible_score_item.description, response.body assert_no_match hidden_score_item.description, response.body assert_match expected_score_string(s1), response.body @@ -309,6 +322,7 @@ def expected_score_string(*args) # Hidden total is not shown evaluation_exercise.update!(visible_score: false) get submission_url(id: submission.id) + assert_match visible_score_item.description, response.body assert_no_match hidden_score_item.description, response.body assert_match expected_score_string(s1), response.body @@ -318,6 +332,7 @@ def expected_score_string(*args) # The evaluation is no longer released evaluation.update!(released: false) get submission_url(id: submission.id) + assert_no_match visible_score_item.description, response.body assert_no_match hidden_score_item.description, response.body assert_no_match expected_score_string(s1), response.body @@ -341,6 +356,7 @@ def expected_score_string(*args) sign_in user get submission_url(id: submission.id) + assert_match visible_score_item.description, response.body assert_match hidden_score_item.description, response.body assert_match expected_score_string(s1), response.body @@ -350,6 +366,7 @@ def expected_score_string(*args) # Hidden total is not shown evaluation_exercise.update!(visible_score: false) get submission_url(id: submission.id) + assert_match visible_score_item.description, response.body assert_match hidden_score_item.description, response.body assert_match expected_score_string(s1), response.body @@ -359,6 +376,7 @@ def expected_score_string(*args) # The evaluation is no longer released evaluation.update!(released: false) get submission_url(id: submission.id) + assert_match visible_score_item.description, response.body assert_match hidden_score_item.description, response.body assert_match expected_score_string(s1), response.body diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index 75dea1ed7f..f04fb76b23 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -26,15 +26,16 @@ class UsersControllerTest < ActionDispatch::IntegrationTest test 'json representation should contain courses' do # create distractions other_user = users(:student) - create(:course, subscribed_members: [other_user]) + create :course, subscribed_members: [other_user] # actual course to test against - create(:course, subscribed_members: [@instance, other_user]) + create :course, subscribed_members: [@instance, other_user] get user_url(@instance, format: :json) assert_response :success user_json = response.parsed_body + assert user_json.key?('subscribed_courses') course_ids = user_json['subscribed_courses'].pluck('id') @@ -42,7 +43,8 @@ class UsersControllerTest < ActionDispatch::IntegrationTest # check if each course in the result actually belongs to the user course_ids.each do |cid| c = Course.find(cid) - assert @instance.subscribed_courses.include?(c), "should not contain #{c}" + + assert_includes @instance.subscribed_courses, c, "should not contain #{c}" end # this should catch the case where there are less courses returned @@ -77,8 +79,9 @@ class UsersControllerTest < ActionDispatch::IntegrationTest end test 'user index with course_id should be ok' do - course = create(:course) + course = create :course get users_url(course_id: course.id) + assert_response :success end diff --git a/test/factories/annotations.rb b/test/factories/annotations.rb index e1a7c4b346..fa9b1e1e5c 100644 --- a/test/factories/annotations.rb +++ b/test/factories/annotations.rb @@ -33,9 +33,7 @@ end trait :with_evaluation do - evaluation do - create :evaluation - end + evaluation end end end diff --git a/test/factories/evaluations.rb b/test/factories/evaluations.rb index f9904a16a4..a4166ec3c8 100644 --- a/test/factories/evaluations.rb +++ b/test/factories/evaluations.rb @@ -11,7 +11,7 @@ # FactoryBot.define do factory :evaluation do - series { create :series, deadline: DateTime.now - 1.minute } + association :series, deadline: DateTime.now - 1.minute deadline { series.deadline || (DateTime.now - 1.minute) } released { false } exercises { series.exercises } diff --git a/test/helpers/activity_helper_test.rb b/test/helpers/activity_helper_test.rb index 4700aeddab..11aa3363b1 100644 --- a/test/helpers/activity_helper_test.rb +++ b/test/helpers/activity_helper_test.rb @@ -12,6 +12,7 @@ class ActivityHelperTest < ActiveSupport::TestCase test 'previous activity at beginning of series should be nil' do current_exercise = @series.exercises[0] previous_ex_path, = previous_next_activity_path(@series, current_exercise) + assert_nil previous_ex_path end @@ -21,6 +22,7 @@ class ActivityHelperTest < ActiveSupport::TestCase previous_exercise_path = course_series_activity_path(I18n.locale, @series.course_id, @series, previous_exercise) previous_ex_path, = previous_next_activity_path(@series, current_exercise) + assert_equal previous_exercise_path, previous_ex_path end @@ -30,12 +32,14 @@ class ActivityHelperTest < ActiveSupport::TestCase next_exercise_path = course_series_activity_path(I18n.locale, @series.course_id, @series, next_exercise) _, next_ex_path = previous_next_activity_path(@series, current_exercise) + assert_equal next_exercise_path, next_ex_path end test 'next activity at end of series should be nil' do current_exercise = @series.exercises[2] _, next_ex_path = previous_next_activity_path(@series, current_exercise) + assert_nil next_ex_path end @@ -59,10 +63,12 @@ def check_description(description) def check_footnotes(footnotes) footnote_a = footnotes.to_a index, content = footnote_a[0] + assert_equal '1', index assert_equal 'https://google.com', content index, content = footnote_a[1] + assert_equal '2', index assert_equal 'http://example.com/activities/123455/', content end @@ -82,7 +88,7 @@ def check_footnotes(footnotes) exercise = create :exercise, description_html_stubbed: desc with_renderer_for exercise do |r| - assert r.footnote_urls.empty? + assert_empty r.footnote_urls end end @@ -91,7 +97,7 @@ def check_footnotes(footnotes) exercise = create :exercise, description_html_stubbed: desc with_renderer_for exercise do |r| - assert r.footnote_urls.empty? + assert_empty r.footnote_urls assert_equal desc, r.description_html end end diff --git a/test/helpers/application_helper_test.rb b/test/helpers/application_helper_test.rb index d5ae502096..6c4295191c 100644 --- a/test/helpers/application_helper_test.rb +++ b/test/helpers/application_helper_test.rb @@ -74,6 +74,7 @@ class ApplicationHelperTest < ActiveSupport::TestCase
Hello HTML clean_html = sanitize dirty_html + assert_no_match(/`') visit exercise_path(id: @instance.id) + assert_text '``' - create(:submission, exercise: @instance, user: @user, status: :correct, code: 'print("😀")') + create :submission, exercise: @instance, user: @user, status: :correct, code: 'print("😀")' visit exercise_path(id: @instance.id) + assert_text 'print("😀")' assert_text 'Restore the initial code.' find('a', text: 'Restore the initial code.').click + assert_text '``' end end diff --git a/test/system/annotations_test.rb b/test/system/annotations_test.rb index 38fbe9f897..865c96f620 100644 --- a/test/system/annotations_test.rb +++ b/test/system/annotations_test.rb @@ -9,7 +9,7 @@ class AnnotationsTest < ApplicationSystemTestCase include Capybara::Minitest::Assertions setup do - @zeus = create(:zeus) + @zeus = create :zeus sign_in @zeus @code_lines = Faker::Lorem.sentences(number: 5) @instance = create :correct_submission, result: Rails.root.join('db/results/python-result.json').read, code: @code_lines.join("\n"), course: create(:course) @@ -19,6 +19,7 @@ class AnnotationsTest < ApplicationSystemTestCase test 'Can view submission page' do visit(submission_path(id: @instance.id)) + within '.card-title' do assert_text 'Submission results' end @@ -50,7 +51,8 @@ class AnnotationsTest < ApplicationSystemTestCase (1..@code_lines.length).each do |index| line = "tr#line-#{index}" find(line).hover - assert_css '.annotation-button button' + + assert_css '.annotation-button a' end end end @@ -60,12 +62,13 @@ class AnnotationsTest < ApplicationSystemTestCase click_link 'Code' find('tr#line-1').hover - find('.annotation-button button').click + find('.annotation-button a').click within '.code-listing' do @code_lines.each do |code_line| assert_text code_line end end + assert_css 'd-annotation-form' end @@ -74,7 +77,7 @@ class AnnotationsTest < ApplicationSystemTestCase click_link 'Code' find('tr#line-1').hover - find('.annotation-button button').click + find('.annotation-button a').click initial = 'This is a single line comment' within 'form.annotation-submission' do @@ -94,7 +97,7 @@ class AnnotationsTest < ApplicationSystemTestCase click_link 'Code' find('tr#line-1').hover - find('.annotation-button button').click + find('.annotation-button a').click initial = 'This is a single line comment' within 'form.annotation-submission' do @@ -111,7 +114,7 @@ class AnnotationsTest < ApplicationSystemTestCase click_link 'Code' find('tr#line-1').hover - find('.annotation-button button').click + find('.annotation-button a').click within 'form.annotation-submission' do click_button 'Cancel' end @@ -125,6 +128,7 @@ class AnnotationsTest < ApplicationSystemTestCase visit(submission_path(id: @instance.id)) click_link 'Code' + within '.annotation' do assert_text annot.annotation_text end @@ -194,6 +198,7 @@ class AnnotationsTest < ApplicationSystemTestCase annot = create :annotation, submission: @instance, user: @zeus visit(submission_path(id: @instance.id)) click_link 'Code' + assert_selector '.annotation', count: 1 within '.annotation' do assert_text annot.annotation_text @@ -204,6 +209,7 @@ class AnnotationsTest < ApplicationSystemTestCase dropdown.find('i.mdi.mdi-pencil').click replacement = Faker::Lorem.characters number: 10_010 + assert_selector 'form.annotation-submission', count: 1 # Attempt to type more than 10.000 characters. within 'form.annotation-submission' do @@ -248,6 +254,7 @@ class AnnotationsTest < ApplicationSystemTestCase # After reload, make sure no replacing has taken place visit(submission_path(id: @instance.id)) click_link 'Code' + assert_selector '.annotation', count: 1 within '.annotation' do assert_text annot.annotation_text @@ -261,7 +268,7 @@ class AnnotationsTest < ApplicationSystemTestCase click_link 'Code' find('tr#line-1').hover - find('.annotation-button button').click + find('.annotation-button a').click initial = '' within 'form.annotation-submission' do @@ -273,11 +280,13 @@ class AnnotationsTest < ApplicationSystemTestCase click_button 'Cancel' end + assert_selector '.annotation', count: 0 # After reload, make sure no creation has taken place visit(submission_path(id: @instance.id)) click_link 'Code' + assert_selector '.annotation', count: 0 end @@ -286,7 +295,7 @@ class AnnotationsTest < ApplicationSystemTestCase click_link 'Code' find('tr#line-1').hover - find('.annotation-button button').click + find('.annotation-button a').click initial = Faker::Lorem.characters(number: 10_010) within 'form.annotation-submission' do @@ -320,6 +329,7 @@ class AnnotationsTest < ApplicationSystemTestCase # After reload, make sure creation has taken place visit(submission_path(id: @instance.id)) click_link 'Code' + assert_selector '.annotation', count: 1 within '.annotation' do assert_text initial @@ -354,6 +364,7 @@ class AnnotationsTest < ApplicationSystemTestCase # After reload, make sure creation has taken place visit(submission_path(id: @instance.id)) click_link 'Code' + assert_selector '.annotation', count: 1 within '.annotation' do assert_text replacement @@ -395,6 +406,7 @@ class AnnotationsTest < ApplicationSystemTestCase root = first('.annotation') dropdown = root.find('.dropdown') dropdown.click + assert_no_selector 'i.mdi.mdi-delete' end end @@ -418,6 +430,7 @@ class AnnotationsTest < ApplicationSystemTestCase root = first('.annotation') dropdown = root.find('.dropdown') dropdown.click + assert_selector 'i.mdi.mdi-delete' end end diff --git a/test/system/courses_test.rb b/test/system/courses_test.rb index 5e220497ac..1ae53a6657 100644 --- a/test/system/courses_test.rb +++ b/test/system/courses_test.rb @@ -9,7 +9,7 @@ class CoursesTest < ApplicationSystemTestCase include Capybara::Minitest::Assertions test 'Can view courses page with working tabs' do - zeus = create(:zeus, :with_institution) + zeus = create :zeus, :with_institution c1 = create :course, series_count: 1, activities_per_series: 1, submissions_per_exercise: 1 c2 = create :course, series_count: 1, activities_per_series: 1, submissions_per_exercise: 1 c3 = create :course, series_count: 1, activities_per_series: 1, submissions_per_exercise: 1 @@ -20,14 +20,17 @@ class CoursesTest < ApplicationSystemTestCase sign_in zeus visit(courses_path) + assert_selector 'd-filter-tabs li', count: 4 assert_selector 'd-filter-tabs li:first-child a.active' assert_selector '#courses-table-wrapper tbody tr', count: 2 find('d-filter-tabs').click_link 'All courses' + assert_selector 'd-filter-tabs li:nth-of-type(3) a.active' assert_selector '#courses-table-wrapper tbody tr', count: 4 find('d-filter-tabs').click_link 'My courses' + assert_selector 'd-filter-tabs li:nth-of-type(4) a.active' assert_selector '#courses-table-wrapper tbody tr', count: 1 end @@ -35,6 +38,7 @@ class CoursesTest < ApplicationSystemTestCase test 'Can view content of visible_for_all course when not logged in' do course = create :course, visibility: :visible_for_all, series_count: 3, activities_per_series: 2, submissions_per_exercise: 1 visit(course_path(:en, course.id)) + assert_selector 'h2', text: course.name assert_selector '.card.series', count: 3 diff --git a/test/system/feedbacks_test.rb b/test/system/feedbacks_test.rb index f7c11fa94d..b8bddb3d53 100644 --- a/test/system/feedbacks_test.rb +++ b/test/system/feedbacks_test.rb @@ -62,6 +62,7 @@ class FeedbacksTest < ApplicationSystemTestCase second_input = find(id: "#{@score_item_second.id}-score-form-wrapper").find('.score-input:not(.in-progress)') @score.reload + assert_equal BigDecimal('9.0'), @score.score # Add new score for second score_item @@ -88,6 +89,7 @@ class FeedbacksTest < ApplicationSystemTestCase find(id: "#{@score_item_first.id}-score-form-wrapper").find('.score-input:not(.in-progress)') @score.reload + assert_equal BigDecimal('6'), @score.score end diff --git a/test/system/questions_test.rb b/test/system/questions_test.rb index a6d037fb8f..a6ab75629b 100644 --- a/test/system/questions_test.rb +++ b/test/system/questions_test.rb @@ -36,8 +36,9 @@ class QuestionsTest < ApplicationSystemTestCase line_element.hover within line_element do - button = find('.annotation-button button') + button = find('.annotation-button a') button.click + assert_css 'form.annotation-submission' # cancel form to limit page space taken within 'form.annotation-submission' do @@ -54,6 +55,7 @@ class QuestionsTest < ApplicationSystemTestCase within '.code-table' do click_button 'Ask a question about your code' + assert_css 'form.annotation-submission' end end @@ -82,14 +84,16 @@ class QuestionsTest < ApplicationSystemTestCase assert_equal 1, Question.count, 'Too little or too many questions were created' q = Question.first + assert_equal q.question_text, question, 'Something went wrong in saving the question' - assert q.unanswered?, 'Should be an unanswered question' + assert_predicate q, :unanswered?, 'Should be an unanswered question' end test 'student can mark a question as resolved' do q = create :question, submission: @submission, user: @student + assert_equal 1, Question.count, 'Test is invalid if magically no or more questions appear here' - assert q.unanswered?, 'Question should start as unanswered' + assert_predicate q, :unanswered?, 'Question should start as unanswered' visit(submission_path(id: @submission.id)) click_link 'Code' @@ -98,19 +102,22 @@ class QuestionsTest < ApplicationSystemTestCase within thread do resolve_button = find('.btn', text: 'Mark as answered') resolve_button.click + assert_no_css '.mdi-comment-question-outline' end assert_equal 1, Question.count, 'There should still only be one question' q = Question.first + assert_not q.unanswered?, 'Question should have moved onto answered status' - assert q.answered?, 'Question should have moved onto answered status' + assert_predicate q, :answered?, 'Question should have moved onto answered status' end test 'Responding to a question should mark the question as answered' do q = create :question, submission: @submission, user: @student + assert_equal 1, Question.count, 'Test is invalid if magically no or more questions appear here' - assert q.unanswered?, 'Question should start as unanswered' + assert_predicate q, :unanswered?, 'Question should start as unanswered' visit(submission_path(id: @submission.id)) click_link 'Code' @@ -133,18 +140,20 @@ class QuestionsTest < ApplicationSystemTestCase assert_equal 2, Question.count, 'There should be two questions now' assert_not q.reload.unanswered?, 'Question should have moved onto answered status' - assert q.reload.answered?, 'Question should have moved onto answered status' + assert_predicate q.reload, :answered?, 'Question should have moved onto answered status' end test 'An unanswered question should contain an icon to visualize its status' do q = create :question, submission: @submission, user: @student + assert_equal 1, Question.count, 'Test is invalid if magically no or more questions appear here' - assert q.unanswered?, 'Question should start as unanswered' + assert_predicate q, :unanswered?, 'Question should start as unanswered' visit(submission_path(id: @submission.id)) click_link 'Code' thread = find('d-thread') + within thread do assert_selector '.mdi-comment-question-outline' end @@ -152,8 +161,9 @@ class QuestionsTest < ApplicationSystemTestCase test 'The status icon should change to in progress when someone clicks reply' do q = create :question, submission: @submission, user: @student + assert_equal 1, Question.count, 'Test is invalid if magically no or more questions appear here' - assert q.unanswered?, 'Question should start as unanswered' + assert_predicate q, :unanswered?, 'Question should start as unanswered' visit(submission_path(id: @submission.id)) click_link 'Code' @@ -163,15 +173,17 @@ class QuestionsTest < ApplicationSystemTestCase assert_selector '.mdi-comment-question-outline' fake_answer_input = find('input') fake_answer_input.click + assert_selector '.mdi-comment-processing-outline' - assert q.reload.in_progress?, 'Question should have moved onto in progress status' + assert_predicate q.reload, :in_progress?, 'Question should have moved onto in progress status' end end test 'The question becomes unanswered again when a teacher cancels the reply' do q = create :question, submission: @submission, user: @student + assert_equal 1, Question.count, 'Test is invalid if magically no or more questions appear here' - assert q.unanswered?, 'Question should start as unanswered' + assert_predicate q, :unanswered?, 'Question should start as unanswered' visit(submission_path(id: @submission.id)) click_link 'Code' @@ -181,12 +193,14 @@ class QuestionsTest < ApplicationSystemTestCase assert_selector '.mdi-comment-question-outline' fake_answer_input = find('input') fake_answer_input.click + assert_selector '.mdi-comment-processing-outline' - assert q.reload.in_progress?, 'Question should have moved onto in progress status' + assert_predicate q.reload, :in_progress?, 'Question should have moved onto in progress status' click_button 'Cancel' + assert_selector '.mdi-comment-question-outline' - assert q.reload.unanswered?, 'Question should have moved onto unanswered status' + assert_predicate q.reload, :unanswered?, 'Question should have moved onto unanswered status' end end end diff --git a/test/system/saved_annotation_test.rb b/test/system/saved_annotation_test.rb index b93ee5fe6a..56f56819be 100644 --- a/test/system/saved_annotation_test.rb +++ b/test/system/saved_annotation_test.rb @@ -32,7 +32,7 @@ class SavedAnnotationsTest < ApplicationSystemTestCase click_link 'Code' find('tr#line-1').hover - find('.annotation-button button').click + find('.annotation-button a').click initial = 'The first five words of this comment will be used as the title' within 'form.annotation-submission' do @@ -43,8 +43,9 @@ class SavedAnnotationsTest < ApplicationSystemTestCase # assert checkbox to fill out title assert_css '#check-save-annotation' assert_no_css '#saved-annotation-title' - find('#check-save-annotation').check - assert_equal 'The first five words of', find('#saved-annotation-title').value + find_by_id('check-save-annotation').check + + assert_equal 'The first five words of', find_by_id('saved-annotation-title').value click_button 'Comment' end @@ -62,12 +63,13 @@ class SavedAnnotationsTest < ApplicationSystemTestCase click_link 'Code' find('tr#line-1').hover - find('.annotation-button button').click + find('.annotation-button a').click initial = 'The first five words of this comment will be used as the title' within 'form.annotation-submission' do assert_no_css 'd-saved-annotation-input' find('textarea.annotation-submission-input').fill_in with: initial + assert_no_css '#check-save-annotation' click_button 'Ask question' end @@ -88,12 +90,13 @@ class SavedAnnotationsTest < ApplicationSystemTestCase click_link 'Code' find('tr#line-1').hover - find('.annotation-button button').click + find('.annotation-button a').click within 'form.annotation-submission' do assert_css 'd-saved-annotation-input' find('d-saved-annotation-input input[type="text"]').fill_in with: sa.title + assert find_field('annotation-text', with: sa.annotation_text) assert_equal sa.annotation_text, find('textarea.annotation-submission-input').value diff --git a/test/system/scratchpad_test.rb b/test/system/scratchpad_test.rb index b3f1cd12fe..8bf81b4cd1 100644 --- a/test/system/scratchpad_test.rb +++ b/test/system/scratchpad_test.rb @@ -9,10 +9,10 @@ class ScratchpadTest < ApplicationSystemTestCase include Capybara::Minitest::Assertions setup do - @zeus = create(:zeus) + @zeus = create :zeus @course = create :course - @programming_language = create(:programming_language, name: 'python') - @exercise = create(:exercise, programming_language_id: @programming_language.id) + @programming_language = create :programming_language, name: 'python' + @exercise = create :exercise, programming_language_id: @programming_language.id @course.series << create(:series) @course.series.first.activities << @exercise @@ -20,8 +20,9 @@ class ScratchpadTest < ApplicationSystemTestCase # Open Papyros ready for use visit(course_activity_path(course_id: @course.id, id: @exercise.id)) + assert_selector '#scratchpad-offcanvas-show-btn' - find('#scratchpad-offcanvas-show-btn').click + find_by_id('scratchpad-offcanvas-show-btn').click end def codemirror_send_keys(parent, code) @@ -33,7 +34,7 @@ def codemirror_send_keys(parent, code) # Set code in the editor and run it def run_code(code) - codemirror_send_keys(find('#scratchpad-editor-wrapper'), code) + codemirror_send_keys(find_by_id('scratchpad-editor-wrapper'), code) find_button('__papyros-run-code-btn', disabled: false).click end @@ -41,7 +42,7 @@ def run_code(code) ## Hello World! code = "print(\"Hello World!\")\n" run_code code - output_area = find('#scratchpad-output-wrapper') + output_area = find_by_id('scratchpad-output-wrapper') # First run, so wait longer for output to appear output_area.find('span', text: 'Hello World!', wait: 20) @@ -58,9 +59,9 @@ def run_code(code) # Scratchpad can process user input in batch mode scratchpad_input = 'Batch' # Set the input before the run - find('#__papyros-switch-input-mode').click + find_by_id('__papyros-switch-input-mode').click # input area should be re-rendered - codemirror_send_keys(find('#scratchpad-input-wrapper'), "#{scratchpad_input}\n") + codemirror_send_keys(find_by_id('scratchpad-input-wrapper'), "#{scratchpad_input}\n") run_code '' output_area.find('span', text: scratchpad_input) @@ -76,6 +77,7 @@ def run_code(code) find_button('__papyros-stop-btn', disabled: false).click output_area.find('span', text: 'Start') + assert output_area.has_no_xpath?('.//span', text: 'Stop') end end diff --git a/test/system/search_test.rb b/test/system/search_test.rb index af00b0e8ed..f374e62cf4 100644 --- a/test/system/search_test.rb +++ b/test/system/search_test.rb @@ -22,24 +22,34 @@ def assert_path_with_query(path, **query_params) sign_in create(:zeus) visit root_path visit activities_path + assert_path_with_query activities_path find('d-search-field input').send_keys 'test' + assert_path_with_query activities_path, filter: 'test' find('.next_page').click + assert_path_with_query activities_path, filter: 'test', page: 2 page.go_back + assert_path_with_query activities_path, filter: 'test' page.go_back + page.assert_current_path root_path page.go_forward + assert_path_with_query activities_path, filter: 'test' page.go_forward + assert_path_with_query activities_path, filter: 'test', page: 2 find('d-search-field input').send_keys 's' + assert_path_with_query activities_path, filter: 'tests' page.go_back + assert_path_with_query activities_path, filter: 'test', page: 2 page.go_forward + assert_path_with_query activities_path, filter: 'tests' end @@ -48,8 +58,10 @@ def assert_path_with_query(path, **query_params) visit root_path click_on 'Toggle drawer' click_on 'Exercises' + page.assert_current_path activities_path page.go_back + page.assert_current_path root_path end end diff --git a/test/system/series_visualisations_test.rb b/test/system/series_visualisations_test.rb index 090e2b3240..589c90ba04 100644 --- a/test/system/series_visualisations_test.rb +++ b/test/system/series_visualisations_test.rb @@ -9,7 +9,7 @@ class SeriesVisualisationsTest < ApplicationSystemTestCase include Capybara::Minitest::Assertions setup do - @zeus = create(:zeus) + @zeus = create :zeus @c1 = create :course, series_count: 1, activities_per_series: 1, submissions_per_exercise: 1 @c1.administrating_members.concat(@zeus) @@ -30,6 +30,7 @@ class SeriesVisualisationsTest < ApplicationSystemTestCase end find('.btn.graph-toggle .stacked-bar-chart').click + assert_selector '.btn.graph-toggle.active .stacked-bar-chart' within title do assert_text 'Distribution of submission statuses' @@ -42,12 +43,14 @@ class SeriesVisualisationsTest < ApplicationSystemTestCase # end find('.btn.graph-toggle .stacked-line-chart').click + assert_selector '.btn.graph-toggle.active .stacked-line-chart' within title do assert_text 'Users with at least one correct submission' end find('.btn.graph-toggle .violin').click + assert_selector '.btn.graph-toggle.active .violin' within title do assert_text 'Number of submissions per user' diff --git a/test/tasks/merge_institutions_test.rb b/test/tasks/merge_institutions_test.rb index f4ff84fc4f..790097f27c 100644 --- a/test/tasks/merge_institutions_test.rb +++ b/test/tasks/merge_institutions_test.rb @@ -109,6 +109,7 @@ def merge_institutions_interactive(i1_id, i2_id, *chars) assert User.exists?(u1.id) assert User.exists?(u2.id) u1.reload + assert_equal i2, u1.institution end @@ -130,8 +131,10 @@ def merge_institutions_interactive(i1_id, i2_id, *chars) (overlapping_users_i2 + unique_users_i1 + unique_users_i2).each do |u| assert User.exists?(u.id) u.reload + assert_equal i2, u.institution u.max_one_institution + assert_equal 0, u.errors.count end end @@ -153,15 +156,19 @@ def merge_institutions_interactive(i1_id, i2_id, *chars) (overlapping_users_i1 + unique_users_i1).each do |u| assert User.exists?(u.id) u.reload + assert_equal i1, u.institution u.max_one_institution + assert_equal 0, u.errors.count end (overlapping_users_i2 + unique_users_i2).each do |u| assert User.exists?(u.id) u.reload + assert_equal i2, u.institution u.max_one_institution + assert_equal 0, u.errors.count end end @@ -181,15 +188,19 @@ def merge_institutions_interactive(i1_id, i2_id, *chars) (overlapping_users_i1 + unique_users_i1).each do |u| assert User.exists?(u.id) u.reload + assert_equal i1, u.institution u.max_one_institution + assert_equal 0, u.errors.count end (overlapping_users_i2 + unique_users_i2).each do |u| assert User.exists?(u.id) u.reload + assert_equal i2, u.institution u.max_one_institution + assert_equal 0, u.errors.count end end @@ -207,6 +218,7 @@ def merge_institutions_interactive(i1_id, i2_id, *chars) assert User.exists?(u1.id) assert_not User.exists?(u2.id) u1.reload + assert_equal i2, u1.institution end @@ -227,15 +239,19 @@ def merge_institutions_interactive(i1_id, i2_id, *chars) (overlapping_users_i1 + unique_users_i1).each do |u| assert User.exists?(u.id) u.reload + assert_equal i1, u.institution u.max_one_institution + assert_equal 0, u.errors.count end (overlapping_users_i2 + unique_users_i2).each do |u| assert User.exists?(u.id) u.reload + assert_equal i2, u.institution u.max_one_institution + assert_equal 0, u.errors.count end end @@ -249,13 +265,14 @@ def merge_institutions_interactive(i1_id, i2_id, *chars) create :user, username: 'test1', institution: i2 s = create :correct_submission, user: u1, code: "print(input())\n", result: FILE_LOCATION.read - assert s.on_filesystem? + + assert_predicate s, :on_filesystem? merge_institutions_interactive i1.id, i2.id, 'y', 'y', 'y', 'n' assert Institution.exists?(i1.id) assert Institution.exists?(i2.id) - assert s.on_filesystem? + assert_predicate s, :on_filesystem? end test 'The script should also merge case insensitive equal usernames' do @@ -271,6 +288,7 @@ def merge_institutions_interactive(i1_id, i2_id, *chars) assert_not User.exists?(u1.id) assert User.exists?(u2.id) u2.reload + assert_equal i2, u2.institution end end diff --git a/test/testhelpers/crud_helper.rb b/test/testhelpers/crud_helper.rb index 665be7270d..864a369f53 100644 --- a/test/testhelpers/crud_helper.rb +++ b/test/testhelpers/crud_helper.rb @@ -27,7 +27,7 @@ def last_created # Generates a hash which maps attribute names on valid # attribute values using the model's factory. def generate_attr_hash - instance = build(model_sym) + instance = build model_sym allowed_attrs.index_with { |attr| instance.send(attr) } end @@ -53,32 +53,37 @@ def check_attrs(attr_hash, obj) Actual: "#{actual}" MSG end - assert not_equal.empty?, - <<~MSG - The following attributtes are not equal: - #{not_equal.join "\n"} - MSG + + assert_empty not_equal, + <<~MSG + The following attributtes are not equal: + #{not_equal.join "\n"} + MSG end # Read (show, index, new, edit) def should_show get polymorphic_url(@instance), params: { format: format } + assert_response :success end def should_get_index get polymorphic_url(model), params: { format: format } + assert_response :success end def should_get_new get new_polymorphic_url(model), params: { format: format } + assert_response :success end def should_get_edit get edit_polymorphic_url(@instance), params: { format: format } + assert_response :success end @@ -109,6 +114,7 @@ def should_set_attributes_on_create def should_redirect_on_create create_request_expect + assert_redirected_to polymorphic_url(last_created) end @@ -132,6 +138,7 @@ def should_set_attributes_on_update def should_redirect_on_update update_request + assert_redirected_to polymorphic_url(@instance) end @@ -149,6 +156,7 @@ def should_destroy def should_redirect_on_destroy destroy_request + assert_redirected_to polymorphic_url(model) end end diff --git a/test/testhelpers/delayed_job_helper.rb b/test/testhelpers/delayed_job_helper.rb index b0337dbd3e..06df0ac272 100644 --- a/test/testhelpers/delayed_job_helper.rb +++ b/test/testhelpers/delayed_job_helper.rb @@ -13,6 +13,7 @@ def with_delayed_jobs def run_delayed_jobs Delayed::Job.find_each(batch_size: 100) do |d| Delayed::Worker.new.run(d) + assert_nil d.last_error end end diff --git a/test/testhelpers/export_zip_helper.rb b/test/testhelpers/export_zip_helper.rb index c86dec0095..4a0ad30416 100644 --- a/test/testhelpers/export_zip_helper.rb +++ b/test/testhelpers/export_zip_helper.rb @@ -28,8 +28,9 @@ def assert_zip(zip_data, options = {}) def check_csv(entry) csv = entry.get_input_stream.read header = csv.split("\n").first + %w[filename status submission_id exercise_id name_nl name_en].each do |h| - assert header.include?("\"#{h}\""), "info.csv header did not include #{h}" + assert_includes header, "\"#{h}\"", "info.csv header did not include #{h}" end end @@ -38,12 +39,16 @@ def check_entry(entry, options) utf8_name = entry.name.force_encoding('utf-8') case options[:group_by] when 'user' + assert data[:users].any? { |u| utf8_name.start_with? u.full_name }, "The submissions are not grouped by students but should be, example: #{utf8_name}." when 'exercise', nil + assert data[:exercises].any? { |ex| utf8_name.start_with? ex.name.parameterize }, "The submissions are not grouped by exercise but should be, example: #{utf8_name}." when 'series' + assert data[:course].series.any? { |series| utf8_name.start_with? series.name.parameterize }, "The submissions are not grouped by series but should be, example: #{utf8_name}." when 'course' + assert data[:user].courses.any? { |course| utf8_name.start_with? course.name.parameterize }, "The submissions are not grouped by course but should be, example: #{utf8_name}." else raise ArgumentError, 'Unknown group_by option supplied' diff --git a/yarn.lock b/yarn.lock index 1168e8155e..cc6ad4ec52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,46 +15,46 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" - integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== dependencies: - "@babel/highlight" "^7.22.10" + "@babel/highlight" "^7.22.13" chalk "^2.4.2" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== -"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.22.10", "@babel/core@^7.7.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" - integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== +"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.22.17", "@babel/core@^7.7.5": + version "7.22.17" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.17.tgz#2f9b0b395985967203514b24ee50f9fd0639c866" + integrity sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.10" - "@babel/generator" "^7.22.10" - "@babel/helper-compilation-targets" "^7.22.10" - "@babel/helper-module-transforms" "^7.22.9" - "@babel/helpers" "^7.22.10" - "@babel/parser" "^7.22.10" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.22.15" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.22.17" + "@babel/helpers" "^7.22.15" + "@babel/parser" "^7.22.16" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.22.17" + "@babel/types" "^7.22.17" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.2" + json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" - integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== +"@babel/generator@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.15.tgz#1564189c7ec94cb8f77b5e8a90c4d200d21b2339" + integrity sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA== dependencies: - "@babel/types" "^7.22.10" + "@babel/types" "^7.22.15" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -80,26 +80,26 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.10", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024" - integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q== +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== dependencies: "@babel/compat-data" "^7.22.9" - "@babel/helper-validator-option" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" browserslist "^4.21.9" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.22.10", "@babel/helper-create-class-features-plugin@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3" - integrity sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA== +"@babel/helper-create-class-features-plugin@^7.22.11", "@babel/helper-create-class-features-plugin@^7.22.15", "@babel/helper-create-class-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz#97a61b385e57fe458496fad19f8e63b63c867de4" + integrity sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.15" "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-replace-supers" "^7.22.9" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" @@ -154,6 +154,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-member-expression-to-functions@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.15.tgz#b95a144896f6d491ca7863576f820f3628818621" + integrity sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA== + dependencies: + "@babel/types" "^7.22.15" + "@babel/helper-member-expression-to-functions@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" @@ -161,23 +168,23 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-module-imports@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" - integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== +"@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" - integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== +"@babel/helper-module-transforms@^7.22.15", "@babel/helper-module-transforms@^7.22.17", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.17" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz#7edf129097a51ccc12443adbc6320e90eab76693" + integrity sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ== dependencies: "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-simple-access" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.15" "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" @@ -245,15 +252,20 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-validator-identifier@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044" + integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ== + "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== -"@babel/helper-validator-option@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" - integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== "@babel/helper-wrap-function@^7.22.5": version "7.22.5" @@ -274,51 +286,51 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.10" -"@babel/helpers@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" - integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== +"@babel/helpers@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.15.tgz#f09c3df31e86e3ea0b7ff7556d85cdebd47ea6f1" + integrity sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw== dependencies: - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.22.15" + "@babel/types" "^7.22.15" -"@babel/highlight@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" - integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== +"@babel/highlight@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16" + integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ== dependencies: "@babel/helper-validator-identifier" "^7.22.5" chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" - integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.22.15", "@babel/parser@^7.22.16": + version "7.22.16" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95" + integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" - integrity sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz#02dc8a03f613ed5fdc29fb2f728397c78146c962" + integrity sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" - integrity sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz#2aeb91d337d4e1a1e7ce85b76a37f5301781200f" + integrity sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.15" -"@babel/plugin-proposal-decorators@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.10.tgz#d6a8c3a9018e1b13e6647f869c5ea56ff2b585d4" - integrity sha512-KxN6TqZzcFi4uD3UifqXElBTBNLAEH1l3vzMQj6JwJZbL2sZlThxSViOKCYY+4Ah4V4JhQ95IVB7s/Y6SJSlMQ== +"@babel/plugin-proposal-decorators@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.15.tgz#dc774eae73ab8c28a644d490b45aa47a85bb0bf5" + integrity sha512-kc0VvbbUyKelvzcKOSyQUSVVXS5pT3UhRB0e3c9An86MvLqs+gx0dN4asllrDluqSa3m9YyooXKGOFVomnyFkg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.10" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-replace-supers" "^7.22.9" "@babel/helper-split-export-declaration" "^7.22.6" @@ -502,10 +514,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz#45946cd17f915b10e65c29b8ed18a0a50fc648c8" - integrity sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g== +"@babel/plugin-transform-async-generator-functions@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz#3b153af4a6b779f340d5b80d3f634f55820aefa3" + integrity sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" @@ -528,10 +540,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-block-scoping@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz#88a1dccc3383899eb5e660534a76a22ecee64faa" - integrity sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg== +"@babel/plugin-transform-block-scoping@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.15.tgz#494eb82b87b5f8b1d8f6f28ea74078ec0a10a841" + integrity sha512-G1czpdJBZCtngoK1sJgloLiOHUnkb/bLZwqVZD8kXmq0ZnVfTTWUcs9OWtp0mBtYJ+4LQY1fllqBkOIPhXmFmw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -543,27 +555,27 @@ "@babel/helper-create-class-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-class-static-block@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" - integrity sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA== +"@babel/plugin-transform-class-static-block@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz#dc8cc6e498f55692ac6b4b89e56d87cec766c974" + integrity sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.11" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-transform-classes@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" - integrity sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ== +"@babel/plugin-transform-classes@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz#aaf4753aee262a232bbc95451b4bdf9599c65a0b" + integrity sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" @@ -575,10 +587,10 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/template" "^7.22.5" -"@babel/plugin-transform-destructuring@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz#38e2273814a58c810b6c34ea293be4973c4eb5e2" - integrity sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw== +"@babel/plugin-transform-destructuring@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.15.tgz#e7404ea5bb3387073b9754be654eecb578324694" + integrity sha512-HzG8sFl1ZVGTme74Nw+X01XsUTqERVQ6/RLHo3XjGRzm7XD6QTtfS3NJotVgCGy8BzkDqRjRBD8dAyJn5TuvSQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -597,10 +609,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dynamic-import@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" - integrity sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ== +"@babel/plugin-transform-dynamic-import@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz#2c7722d2a5c01839eaf31518c6ff96d408e447aa" + integrity sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" @@ -613,18 +625,18 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-export-namespace-from@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" - integrity sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg== +"@babel/plugin-transform-export-namespace-from@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz#b3c84c8f19880b6c7440108f8929caf6056db26c" + integrity sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-for-of@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" - integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== +"@babel/plugin-transform-for-of@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz#f64b4ccc3a4f131a996388fae7680b472b306b29" + integrity sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -637,10 +649,10 @@ "@babel/helper-function-name" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-json-strings@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" - integrity sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A== +"@babel/plugin-transform-json-strings@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz#689a34e1eed1928a40954e37f74509f48af67835" + integrity sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-json-strings" "^7.8.3" @@ -652,10 +664,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-logical-assignment-operators@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" - integrity sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA== +"@babel/plugin-transform-logical-assignment-operators@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz#24c522a61688bde045b7d9bc3c2597a4d948fc9c" + integrity sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -675,22 +687,22 @@ "@babel/helper-module-transforms" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" - integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== +"@babel/plugin-transform-modules-commonjs@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.15.tgz#b11810117ed4ee7691b29bd29fd9f3f98276034f" + integrity sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg== dependencies: - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-modules-systemjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" - integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== +"@babel/plugin-transform-modules-systemjs@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz#3386be5875d316493b517207e8f1931d93154bb1" + integrity sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA== dependencies: "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.9" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.5" @@ -717,32 +729,32 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" - integrity sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA== +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz#debef6c8ba795f5ac67cd861a81b744c5d38d9fc" + integrity sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-transform-numeric-separator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" - integrity sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g== +"@babel/plugin-transform-numeric-separator@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz#498d77dc45a6c6db74bb829c02a01c1d719cbfbd" + integrity sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-transform-object-rest-spread@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" - integrity sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ== +"@babel/plugin-transform-object-rest-spread@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz#21a95db166be59b91cde48775310c0df6e1da56f" + integrity sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q== dependencies: - "@babel/compat-data" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" + "@babel/compat-data" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-parameters" "^7.22.15" "@babel/plugin-transform-object-super@^7.22.5": version "7.22.5" @@ -752,27 +764,27 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-replace-supers" "^7.22.5" -"@babel/plugin-transform-optional-catch-binding@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" - integrity sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg== +"@babel/plugin-transform-optional-catch-binding@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz#461cc4f578a127bb055527b3e77404cad38c08e0" + integrity sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.22.10", "@babel/plugin-transform-optional-chaining@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz#076d28a7e074392e840d4ae587d83445bac0372a" - integrity sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g== +"@babel/plugin-transform-optional-chaining@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.15.tgz#d7a5996c2f7ca4ad2ad16dbb74444e5c4385b1ba" + integrity sha512-ngQ2tBhq5vvSJw2Q2Z9i7ealNkpDMU0rGWnHPKqRZO0tzZ5tlaoz4hDvhXioOoaE0X2vfNss1djwg0DXlfu30A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" - integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== +"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz#719ca82a01d177af358df64a514d64c2e3edb114" + integrity sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -784,13 +796,13 @@ "@babel/helper-create-class-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-private-property-in-object@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" - integrity sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ== +"@babel/plugin-transform-private-property-in-object@^7.22.11": + version "7.22.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz#ad45c4fc440e9cb84c718ed0906d96cf40f9a4e1" + integrity sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.11" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" @@ -816,12 +828,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-runtime@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.10.tgz#89eda6daf1d3af6f36fb368766553054c8d7cd46" - integrity sha512-RchI7HePu1eu0CYNKHHHQdfenZcM4nz8rew5B1VWqeRKdcwW5aQ5HeG9eTUbWiAS1UrmHVLmoxTWHt3iLD/NhA== +"@babel/plugin-transform-runtime@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.15.tgz#3a625c4c05a39e932d7d34f5d4895cdd0172fdc9" + integrity sha512-tEVLhk8NRZSmwQ0DJtxxhTrCht1HVo8VaMzYT4w6lwyKBuHsgoioAUA7/6eT2fRfc5/23fuGdlwIxXhRVgWr4g== dependencies: - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" babel-plugin-polyfill-corejs2 "^0.4.5" babel-plugin-polyfill-corejs3 "^0.8.3" @@ -864,13 +876,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-typescript@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz#5c0f7adfc1b5f38c4dbc8f79b1f0f8074134bd7d" - integrity sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA== +"@babel/plugin-transform-typescript@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz#15adef906451d86349eb4b8764865c960eb54127" + integrity sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-typescript" "^7.22.5" @@ -905,17 +917,17 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/preset-env@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.10.tgz#3263b9fe2c8823d191d28e61eac60a79f9ce8a0f" - integrity sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A== +"@babel/preset-env@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.15.tgz#142716f8e00bc030dae5b2ac6a46fbd8b3e18ff8" + integrity sha512-tZFHr54GBkHk6hQuVA8w4Fmq+MSPsfvMG0vPnOYyTnJpyfMqybL8/MbNCPRT9zc2KBO2pe4tq15g6Uno4Jpoag== dependencies: "@babel/compat-data" "^7.22.9" - "@babel/helper-compilation-targets" "^7.22.10" + "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.5" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.15" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.15" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" @@ -936,41 +948,41 @@ "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.22.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.10" + "@babel/plugin-transform-async-generator-functions" "^7.22.15" "@babel/plugin-transform-async-to-generator" "^7.22.5" "@babel/plugin-transform-block-scoped-functions" "^7.22.5" - "@babel/plugin-transform-block-scoping" "^7.22.10" + "@babel/plugin-transform-block-scoping" "^7.22.15" "@babel/plugin-transform-class-properties" "^7.22.5" - "@babel/plugin-transform-class-static-block" "^7.22.5" - "@babel/plugin-transform-classes" "^7.22.6" + "@babel/plugin-transform-class-static-block" "^7.22.11" + "@babel/plugin-transform-classes" "^7.22.15" "@babel/plugin-transform-computed-properties" "^7.22.5" - "@babel/plugin-transform-destructuring" "^7.22.10" + "@babel/plugin-transform-destructuring" "^7.22.15" "@babel/plugin-transform-dotall-regex" "^7.22.5" "@babel/plugin-transform-duplicate-keys" "^7.22.5" - "@babel/plugin-transform-dynamic-import" "^7.22.5" + "@babel/plugin-transform-dynamic-import" "^7.22.11" "@babel/plugin-transform-exponentiation-operator" "^7.22.5" - "@babel/plugin-transform-export-namespace-from" "^7.22.5" - "@babel/plugin-transform-for-of" "^7.22.5" + "@babel/plugin-transform-export-namespace-from" "^7.22.11" + "@babel/plugin-transform-for-of" "^7.22.15" "@babel/plugin-transform-function-name" "^7.22.5" - "@babel/plugin-transform-json-strings" "^7.22.5" + "@babel/plugin-transform-json-strings" "^7.22.11" "@babel/plugin-transform-literals" "^7.22.5" - "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.11" "@babel/plugin-transform-member-expression-literals" "^7.22.5" "@babel/plugin-transform-modules-amd" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.5" - "@babel/plugin-transform-modules-systemjs" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.15" + "@babel/plugin-transform-modules-systemjs" "^7.22.11" "@babel/plugin-transform-modules-umd" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" "@babel/plugin-transform-new-target" "^7.22.5" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" - "@babel/plugin-transform-numeric-separator" "^7.22.5" - "@babel/plugin-transform-object-rest-spread" "^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.11" + "@babel/plugin-transform-numeric-separator" "^7.22.11" + "@babel/plugin-transform-object-rest-spread" "^7.22.15" "@babel/plugin-transform-object-super" "^7.22.5" - "@babel/plugin-transform-optional-catch-binding" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.10" - "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-optional-catch-binding" "^7.22.11" + "@babel/plugin-transform-optional-chaining" "^7.22.15" + "@babel/plugin-transform-parameters" "^7.22.15" "@babel/plugin-transform-private-methods" "^7.22.5" - "@babel/plugin-transform-private-property-in-object" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.11" "@babel/plugin-transform-property-literals" "^7.22.5" "@babel/plugin-transform-regenerator" "^7.22.10" "@babel/plugin-transform-reserved-words" "^7.22.5" @@ -984,7 +996,7 @@ "@babel/plugin-transform-unicode-regex" "^7.22.5" "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" "@babel/preset-modules" "0.1.6-no-external-plugins" - "@babel/types" "^7.22.10" + "@babel/types" "^7.22.15" babel-plugin-polyfill-corejs2 "^0.4.5" babel-plugin-polyfill-corejs3 "^0.8.3" babel-plugin-polyfill-regenerator "^0.5.2" @@ -1000,16 +1012,16 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-typescript@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz#16367d8b01d640e9a507577ed4ee54e0101e51c8" - integrity sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ== +"@babel/preset-typescript@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.15.tgz#43db30516fae1d417d748105a0bc95f637239d48" + integrity sha512-HblhNmh6yM+cU4VwbBRpxFhxsTdfS1zsvH9W+gEjD0ARV9+8B4sNfpI6GuhePti84nuvhiwKS539jKPFHskA9A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.5" - "@babel/plugin-transform-typescript" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.15" + "@babel/plugin-transform-typescript" "^7.22.15" "@babel/regjsgen@^0.8.0": version "0.8.0" @@ -1023,38 +1035,38 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.22.5", "@babel/template@^7.3.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" - integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== +"@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.22.10", "@babel/traverse@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" - integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.22.15", "@babel/traverse@^7.22.17", "@babel/traverse@^7.22.5": + version "7.22.17" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.17.tgz#b23c203ab3707e3be816043081b4a994fcacec44" + integrity sha512-xK4Uwm0JnAMvxYZxOVecss85WxTEIbTa7bnGyf/+EgCL5Zt3U7htUpEOWv9detPlamGKuRzCqw74xVglDWpPdg== dependencies: - "@babel/code-frame" "^7.22.10" - "@babel/generator" "^7.22.10" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.22.15" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.10" - "@babel/types" "^7.22.10" + "@babel/parser" "^7.22.16" + "@babel/types" "^7.22.17" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" - integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.17", "@babel/types@^7.22.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.22.17" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.17.tgz#f753352c4610ffddf9c8bc6823f9ff03e2303eee" + integrity sha512-YSQPHLFtQNE5xN9tHuZnzu8vPr61wVTBZdfv1meex1NBosa4iT05k/Jw06ddJugi4bk7The/oSwQGFcksmEJQg== dependencies: "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.15" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1165,20 +1177,20 @@ style-mod "^4.0.0" w3c-keyname "^2.2.4" -"@csstools/css-parser-algorithms@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.0.tgz#0cc3a656dc2d638370ecf6f98358973bfbd00141" - integrity sha512-dTKSIHHWc0zPvcS5cqGP+/TPFUJB0ekJ9dGKvMAFoNuBFhDPBt9OMGNZiIA5vTiNdGHHBeScYPXIGBMnVOahsA== +"@csstools/css-parser-algorithms@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.1.tgz#ec4fc764ba45d2bb7ee2774667e056aa95003f3a" + integrity sha512-xrvsmVUtefWMWQsGgFffqWSK03pZ1vfDki4IVIIUxxDKnGBzqNgv0A7SB1oXtVNEkcVO8xi1ZrTL29HhSu5kGA== -"@csstools/css-tokenizer@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz#07ae11a0a06365d7ec686549db7b729bc036528e" - integrity sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA== +"@csstools/css-tokenizer@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.2.0.tgz#9d70e6dcbe94e44c7400a2929928db35c4de32b5" + integrity sha512-wErmsWCbsmig8sQKkM6pFhr/oPha1bHfvxsUY5CYSQxwyhA9Ulrs8EqCgClhg4Tgg2XapVstGqSVcz0xOYizZA== -"@csstools/media-query-list-parser@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.2.tgz#6ef642b728d30c1009bfbba3211c7e4c11302728" - integrity sha512-M8cFGGwl866o6++vIY7j1AKuq9v57cf+dGepScwCcbut9ypJNr4Cj+LLTWligYUZ0uyhEoJDKt5lvyBfh2L3ZQ== +"@csstools/media-query-list-parser@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.4.tgz#0017f99945f6c16dd81a7aacf6821770933c3a5c" + integrity sha512-V/OUXYX91tAC1CDsiY+HotIcJR+vPtzrX8pCplCpT++i8ThZZsq5F5dzZh/bDM3WUOjrvC1ljed1oSJxMfjqhw== "@csstools/selector-specificity@^3.0.0": version "3.0.0" @@ -1240,15 +1252,15 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@^8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.47.0.tgz#5478fdf443ff8158f9de171c704ae45308696c7d" - integrity sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og== +"@eslint/js@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333" + integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== -"@humanwhocodes/config-array@^0.11.10": - version "0.11.10" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" - integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== +"@humanwhocodes/config-array@^0.11.11": + version "0.11.11" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" + integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" @@ -1612,17 +1624,17 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== -"@rails/activestorage@^7.0.7": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@rails/activestorage/-/activestorage-7.0.7.tgz#a23e22bccd61756ba52aff3b8cb43b050ae1ecca" - integrity sha512-EoiGMWUmRzYQhOQ1ifTFHuYNdOHP3f0YOhrKsFlshrV8vzkRkip1Ks3BMzW98cYOw7v6PUPmEeBxgb4jY2U2+g== +"@rails/activestorage@^7.0.8": + version "7.0.8" + resolved "https://registry.yarnpkg.com/@rails/activestorage/-/activestorage-7.0.8.tgz#7ea8a656b5621e0dc57f51e3ad2f06c3af30d3ea" + integrity sha512-CxPyCxTV0HIaZP8qqH11tkESNl8TrQsIAesAgeOXDOl0BkaN6nC0/Mq2/0ngezBu9CZbFzfHtP2g6Yl8BWkV8g== dependencies: spark-md5 "^3.0.1" -"@rails/ujs@^7.0.7": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.0.7.tgz#54af8d66160a8a7bf7d8f184703d2bf4b3fab914" - integrity sha512-J2v5Ca7HgejO7diGKiDylaVDQKmbQ5FJih6Oo3hXuBKEuXlcaccJu64lj8MNVLaPVyZx0g4gaOQZQz95QEb/hg== +"@rails/ujs@^7.0.8": + version "7.0.8" + resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.0.8.tgz#59853367d0827b3955d2c4bedfd5eba4a79d3422" + integrity sha512-tOQQBVH8LsUpGXqDnk+kaOGVsgZ8maHAhEiw3Git3p88q+c0Slgu47HuDnL6sVxeCfz24zbq7dOjsVYDiTpDIA== "@sinonjs/commons@^1.7.0": version "1.8.3" @@ -2032,10 +2044,10 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.12.tgz#920447fdd78d76b19de0438b7f60df3c4a80bf1c" integrity sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A== -"@types/serviceworker@^0.0.72": - version "0.0.72" - resolved "https://registry.yarnpkg.com/@types/serviceworker/-/serviceworker-0.0.72.tgz#d4f9f02ab8bc03a9a8075b05ffe0671da1285991" - integrity sha512-RL8/s2CAx+DLmTp00xzzFMiSChkDZD0UgqTwcYYsGdbYWEc0zoBDpJ4lr7gq67S1F3N8QC8EajSzS/5sIkJAcQ== +"@types/serviceworker@^0.0.73": + version "0.0.73" + resolved "https://registry.yarnpkg.com/@types/serviceworker/-/serviceworker-0.0.73.tgz#3da49bc09f02a207a38989c380b690824fe99dd9" + integrity sha512-LwiphGQ4OKMBjNNdVx0AXj+Pc9K/4kKlVupR4xbC9c+n5DYrumMhT/eo1QxQwkpniUupekKSGA46l/1K22/6Gw== "@types/stack-utils@^2.0.0": version "2.0.1" @@ -3028,10 +3040,10 @@ core-js-compat@^3.31.0: dependencies: browserslist "^4.21.9" -core-js@^3.32.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.0.tgz#7643d353d899747ab1f8b03d2803b0312a0fb3b6" - integrity sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww== +core-js@^3.32.2: + version "3.32.2" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.2.tgz#172fb5949ef468f93b4be7841af6ab1f21992db7" + integrity sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ== cosmiconfig@^7.0.0: version "7.1.0" @@ -3824,16 +3836,16 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.47.0: - version "8.47.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.47.0.tgz#c95f9b935463fb4fad7005e626c7621052e90806" - integrity sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q== +eslint@^8.49.0: + version "8.49.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.49.0.tgz#09d80a89bdb4edee2efcf6964623af1054bf6d42" + integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" "@eslint/eslintrc" "^2.1.2" - "@eslint/js" "^8.47.0" - "@humanwhocodes/config-array" "^0.11.10" + "@eslint/js" "8.49.0" + "@humanwhocodes/config-array" "^0.11.11" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" ajv "^6.12.4" @@ -4027,10 +4039,10 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.9, fast-glob@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" - integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== +fast-glob@^3.2.9, fast-glob@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -5628,7 +5640,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json5@2.x, json5@^2.2.2: +json5@2.x, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -5662,10 +5674,10 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -known-css-properties@^0.27.0: - version "0.27.0" - resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.27.0.tgz#82a9358dda5fe7f7bd12b5e7142c0a205393c0c5" - integrity sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg== +known-css-properties@^0.28.0: + version "0.28.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.28.0.tgz#8a8be010f368b3036fe6ab0ef4bbbed972bd6274" + integrity sha512-9pSL5XB4J+ifHP0e0jmmC98OGC1nL8/JjS+fi6mnTlIf//yt/MfVLtKg7S6nCtj/8KTcWX7nRlY0XywoYY1ISQ== leven@^3.1.0: version "3.1.0" @@ -5749,22 +5761,47 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw== + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g== + lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== +lodash.invokemap@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.invokemap/-/lodash.invokemap-4.6.0.tgz#1748cda5d8b0ef8369c4eb3ec54c21feba1f2d62" + integrity sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.pullall@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.pullall/-/lodash.pullall-4.2.0.tgz#9d98b8518b7c965b0fae4099bd9fb7df8bbf38ba" + integrity sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg== + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@4.x, lodash@^4.17.20, lodash@^4.7.0: +lodash.uniqby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" + integrity sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww== + +lodash@4.x, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6441,10 +6478,10 @@ postcss-safe-parser@^6.0.0: resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== -postcss-scss@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.6.tgz#5d62a574b950a6ae12f2aa89b60d63d9e4432bfd" - integrity sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ== +postcss-scss@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.7.tgz#cfe5507aaff81b3d8992039ad015da4bd3dccd2f" + integrity sha512-xPv2GseoyXPa58Nro7M73ZntttusuCmZdeOojUFR5PZDz2BR62vfYx1w9TyOnp1+nYFowgOMipsCBhxzVkAEPw== postcss-selector-parser@^6.0.13: version "6.0.13" @@ -6459,10 +6496,10 @@ postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.25: - version "8.4.27" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.27.tgz#234d7e4b72e34ba5a92c29636734349e0d9c3057" - integrity sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ== +postcss@^8.4.27: + version "8.4.28" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.28.tgz#c6cc681ed00109072816e1557f889ef51cf950a5" + integrity sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -6883,10 +6920,10 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sass@^1.65.1: - version "1.65.1" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.65.1.tgz#8f283b0c26335a88246a448d22e1342ba2ea1432" - integrity sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA== +sass@^1.66.1: + version "1.66.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.66.1.tgz#04b51c4671e4650aa393740e66a4e58b44d055b1" + integrity sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" @@ -7017,14 +7054,14 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.2.tgz#ff55bb1d9ff2114c13b400688fa544ac63c36967" integrity sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q== -sirv@^1.0.7: - version "1.0.19" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" - integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== +sirv@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.3.tgz#ca5868b87205a74bef62a469ed0296abceccd446" + integrity sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA== dependencies: "@polka/url" "^1.0.0-next.20" mrmime "^1.0.0" - totalist "^1.0.0" + totalist "^3.0.0" sisteransi@^1.0.5: version "1.0.5" @@ -7302,39 +7339,27 @@ style-search@^0.1.0: resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" integrity sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg== -stylelint-config-recommended-scss@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-12.0.0.tgz#9d9e82c46012649f11bfebcbc788f58e61860f33" - integrity sha512-5Bb2mlGy6WLa30oNeKpZvavv2lowJUsUJO25+OA68GFTemlwd1zbFsL7q0bReKipOSU3sG47hKneZ6Nd+ctrFA== +stylelint-config-recommended-scss@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-13.0.0.tgz#dd8c319e15a6412262cd8554e4aad9bfba1bbb11" + integrity sha512-7AmMIsHTsuwUQm7I+DD5BGeIgCvqYZ4BpeYJJpb1cUXQwrJAKjA+GBotFZgUEGP8lAM+wmd91ovzOi8xfAyWEw== dependencies: - postcss-scss "^4.0.6" - stylelint-config-recommended "^12.0.0" - stylelint-scss "^5.0.0" - -stylelint-config-recommended@^12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-12.0.0.tgz#d0993232fca017065fd5acfcb52dd8a188784ef4" - integrity sha512-x6x8QNARrGO2sG6iURkzqL+Dp+4bJorPMMRNPScdvaUK8PsynriOcMW7AFDKqkWAS5wbue/u8fUT/4ynzcmqdQ== + postcss-scss "^4.0.7" + stylelint-config-recommended "^13.0.0" + stylelint-scss "^5.1.0" stylelint-config-recommended@^13.0.0: version "13.0.0" resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-13.0.0.tgz#c48a358cc46b629ea01f22db60b351f703e00597" integrity sha512-EH+yRj6h3GAe/fRiyaoO2F9l9Tgg50AOFhaszyfov9v6ayXJ1IkSHwTxd7lB48FmOeSGDPLjatjO11fJpmarkQ== -stylelint-config-standard-scss@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-10.0.0.tgz#159a54a01b80649bf0143fa7ba086b676a1a749e" - integrity sha512-bChBEo1p3xUVWh/wenJI+josoMk21f2yuLDGzGjmKYcALfl2u3DFltY+n4UHswYiXghqXaA8mRh+bFy/q1hQlg== - dependencies: - stylelint-config-recommended-scss "^12.0.0" - stylelint-config-standard "^33.0.0" - -stylelint-config-standard@^33.0.0: - version "33.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-33.0.0.tgz#1f7bb299153a53874073e93829e37a475842f0f9" - integrity sha512-eyxnLWoXImUn77+ODIuW9qXBDNM+ALN68L3wT1lN2oNspZ7D9NVGlNHb2QCUn4xDug6VZLsh0tF8NyoYzkgTzg== +stylelint-config-standard-scss@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-11.0.0.tgz#98332b68a9c98b6fce54c7698741e103719942b5" + integrity sha512-fGE79NBOLg09a9afqGH/guJulRULCaQWWv4cv1v2bMX92B+fGb0y56WqIguwvFcliPmmUXiAhKrrnXilIeXoHA== dependencies: - stylelint-config-recommended "^12.0.0" + stylelint-config-recommended-scss "^13.0.0" + stylelint-config-standard "^34.0.0" stylelint-config-standard@^34.0.0: version "34.0.0" @@ -7343,24 +7368,24 @@ stylelint-config-standard@^34.0.0: dependencies: stylelint-config-recommended "^13.0.0" -stylelint-scss@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-5.0.1.tgz#b33a6580b5734eace083cfc2cc3021225e28547f" - integrity sha512-n87iCRZrr2J7//I/QFsDXxFLnHKw633U4qvWZ+mOW6KDAp/HLj06H+6+f9zOuTYy+MdGdTuCSDROCpQIhw5fvQ== +stylelint-scss@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-5.1.0.tgz#dd318bc5c65f7a11f3ecacc7b6e8b67e7f2f1df1" + integrity sha512-E+KlQFXv1Euha43qw3q+wKBSli557wxbbo6/39DWhRNXlUa9Cz+FYrcgz+PT6ag0l6UisCYjAGCNhoSl4FcwlA== dependencies: postcss-media-query-parser "^0.2.3" postcss-resolve-nested-selector "^0.1.1" postcss-selector-parser "^6.0.13" postcss-value-parser "^4.2.0" -stylelint@^15.10.2: - version "15.10.2" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-15.10.2.tgz#0ee5a8371d3a2e1ff27fefd48309d3ddef7c3405" - integrity sha512-UxqSb3hB74g4DTO45QhUHkJMjKKU//lNUAOWyvPBVPZbCknJ5HjOWWZo+UDuhHa9FLeVdHBZXxu43eXkjyIPWg== +stylelint@^15.10.3: + version "15.10.3" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-15.10.3.tgz#995e4512fdad450fb83e13f3472001f6edb6469c" + integrity sha512-aBQMMxYvFzJJwkmg+BUUg3YfPyeuCuKo2f+LOw7yYbU8AZMblibwzp9OV4srHVeQldxvSFdz0/Xu8blq2AesiA== dependencies: - "@csstools/css-parser-algorithms" "^2.3.0" - "@csstools/css-tokenizer" "^2.1.1" - "@csstools/media-query-list-parser" "^2.1.2" + "@csstools/css-parser-algorithms" "^2.3.1" + "@csstools/css-tokenizer" "^2.2.0" + "@csstools/media-query-list-parser" "^2.1.4" "@csstools/selector-specificity" "^3.0.0" balanced-match "^2.0.0" colord "^2.9.3" @@ -7368,7 +7393,7 @@ stylelint@^15.10.2: css-functions-list "^3.2.0" css-tree "^2.3.1" debug "^4.3.4" - fast-glob "^3.3.0" + fast-glob "^3.3.1" fastest-levenshtein "^1.0.16" file-entry-cache "^6.0.1" global-modules "^2.0.0" @@ -7379,13 +7404,13 @@ stylelint@^15.10.2: import-lazy "^4.0.0" imurmurhash "^0.1.4" is-plain-object "^5.0.0" - known-css-properties "^0.27.0" + known-css-properties "^0.28.0" mathml-tag-names "^2.1.3" meow "^10.1.5" micromatch "^4.0.5" normalize-path "^3.0.0" picocolors "^1.0.0" - postcss "^8.4.25" + postcss "^8.4.27" postcss-resolve-nested-selector "^0.1.1" postcss-safe-parser "^6.0.0" postcss-selector-parser "^6.0.13" @@ -7579,10 +7604,10 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -totalist@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" - integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== tough-cookie@^4.0.0: version "4.1.3" @@ -7871,20 +7896,27 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webpack-bundle-analyzer@^4.9.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.0.tgz#fc093c4ab174fd3dcbd1c30b763f56d10141209d" - integrity sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw== +webpack-bundle-analyzer@^4.9.1: + version "4.9.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.1.tgz#d00bbf3f17500c10985084f22f1a2bf45cb2f09d" + integrity sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w== dependencies: "@discoveryjs/json-ext" "0.5.7" acorn "^8.0.4" acorn-walk "^8.0.0" - chalk "^4.1.0" commander "^7.2.0" + escape-string-regexp "^4.0.0" gzip-size "^6.0.0" - lodash "^4.17.20" + is-plain-object "^5.0.0" + lodash.debounce "^4.0.8" + lodash.escape "^4.0.1" + lodash.flatten "^4.4.0" + lodash.invokemap "^4.6.0" + lodash.pullall "^4.2.0" + lodash.uniqby "^4.7.0" opener "^1.5.2" - sirv "^1.0.7" + picocolors "^1.0.0" + sirv "^2.0.3" ws "^7.3.1" webpack-cli@^4.9.2: