diff --git a/README.md b/README.md index f366789c6e4e2f3586ef0b3c31217620302a1976..8912dda2338b920d2efa185d68aab253e53b2490 100644 --- a/README.md +++ b/README.md @@ -126,29 +126,3 @@ If you want to run this as a mobile application It's possible you will need to add the platforms you want to run and build. The following documentation can be used to run the application in an Android emulator: \ https://cordova.apache.org/docs/en/latest/guide/platforms/android/index.html - -##Tests - -### Django - -To run test for backend enter -`python manage.py test` - - -With coverage: -`coverage run manage.py test` - -`coverage report -m ./users/serializers.py ./workouts/permissions.py` - - -### Cypress - -To run the test in cypress go to the frontend folder and enter in different terminals: - -`npm run test:dev` - -`npm run django:testserver` - -`npm run cypress:open` - - diff --git a/backend/secfit/comments/migrations/0002_auto_20210304_2241.py b/backend/secfit/comments/migrations/0002_auto_20210304_2241.py new file mode 100644 index 0000000000000000000000000000000000000000..0c4fb6d46f3d27664651edc221a883f2e7a01888 --- /dev/null +++ b/backend/secfit/comments/migrations/0002_auto_20210304_2241.py @@ -0,0 +1,24 @@ +# Generated by Django 3.1 on 2021-03-04 22:41 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('comments', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='comment', + name='timestamp', + field=models.DateTimeField(default=django.utils.timezone.now), + ), + migrations.AlterField( + model_name='like', + name='timestamp', + field=models.DateTimeField(default=django.utils.timezone.now), + ), + ] diff --git a/backend/secfit/suggested_workouts/migrations/0002_auto_20210304_2241.py b/backend/secfit/suggested_workouts/migrations/0002_auto_20210304_2241.py new file mode 100644 index 0000000000000000000000000000000000000000..5da3bc244f4fbc5bcb4a4faf37c5cffcc8deed65 --- /dev/null +++ b/backend/secfit/suggested_workouts/migrations/0002_auto_20210304_2241.py @@ -0,0 +1,30 @@ +# Generated by Django 3.1 on 2021-03-04 22:41 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('suggested_workouts', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='suggestedworkout', + name='visibility', + ), + migrations.AddField( + model_name='suggestedworkout', + name='status', + field=models.CharField(choices=[('a', 'Accepted'), ('p', 'Pending'), ('d', 'Declined')], default='p', max_length=8), + ), + migrations.AlterField( + model_name='suggestedworkout', + name='coach', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owner', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/backend/secfit/suggested_workouts/migrations/0003_suggestedworkout_visibility.py b/backend/secfit/suggested_workouts/migrations/0003_suggestedworkout_visibility.py new file mode 100644 index 0000000000000000000000000000000000000000..d2910f0674211fb44aed07a9c21dbd28847a0250 --- /dev/null +++ b/backend/secfit/suggested_workouts/migrations/0003_suggestedworkout_visibility.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1 on 2021-03-04 22:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('suggested_workouts', '0002_auto_20210304_2241'), + ] + + operations = [ + migrations.AddField( + model_name='suggestedworkout', + name='visibility', + field=models.CharField(default='PU', max_length=8), + ), + ] diff --git a/backend/secfit/suggested_workouts/migrations/0004_remove_suggestedworkout_visibility.py b/backend/secfit/suggested_workouts/migrations/0004_remove_suggestedworkout_visibility.py new file mode 100644 index 0000000000000000000000000000000000000000..b954002b01399a9197dbd879f89bf3e0e250e09d --- /dev/null +++ b/backend/secfit/suggested_workouts/migrations/0004_remove_suggestedworkout_visibility.py @@ -0,0 +1,17 @@ +# Generated by Django 3.1 on 2021-03-04 23:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('suggested_workouts', '0003_suggestedworkout_visibility'), + ] + + operations = [ + migrations.RemoveField( + model_name='suggestedworkout', + name='visibility', + ), + ] diff --git a/backend/secfit/suggested_workouts/migrations/0005_suggestedworkout_visibility.py b/backend/secfit/suggested_workouts/migrations/0005_suggestedworkout_visibility.py new file mode 100644 index 0000000000000000000000000000000000000000..782a3ccc84eeb87085b6168e2b1101a7ae8a1e7b --- /dev/null +++ b/backend/secfit/suggested_workouts/migrations/0005_suggestedworkout_visibility.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1 on 2021-03-04 23:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('suggested_workouts', '0004_remove_suggestedworkout_visibility'), + ] + + operations = [ + migrations.AddField( + model_name='suggestedworkout', + name='visibility', + field=models.CharField(blank=True, default='', max_length=8, null=True), + ), + ] diff --git a/backend/secfit/suggested_workouts/migrations/0006_auto_20210305_0929.py b/backend/secfit/suggested_workouts/migrations/0006_auto_20210305_0929.py new file mode 100644 index 0000000000000000000000000000000000000000..3daeb6a801389f183a47dffddb763e489c18c195 --- /dev/null +++ b/backend/secfit/suggested_workouts/migrations/0006_auto_20210305_0929.py @@ -0,0 +1,20 @@ +# Generated by Django 3.1 on 2021-03-05 09:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('suggested_workouts', '0005_suggestedworkout_visibility'), + ] + + operations = [ + migrations.RemoveField( + model_name='suggestedworkout', + name='visibility', + ), + migrations.DeleteModel( + name='WorkoutRequest', + ), + ] diff --git a/backend/secfit/users/migrations/0002_auto_20210304_2241.py b/backend/secfit/users/migrations/0002_auto_20210304_2241.py new file mode 100644 index 0000000000000000000000000000000000000000..007428eed8563af6bd93de6878709da7a3ea4ae7 --- /dev/null +++ b/backend/secfit/users/migrations/0002_auto_20210304_2241.py @@ -0,0 +1,82 @@ +# Generated by Django 3.1 on 2021-03-04 22:41 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import users.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='AthleteFile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file', models.FileField(upload_to=users.models.athlete_directory_path)), + ], + ), + migrations.RemoveField( + model_name='offer', + name='offer_type', + ), + migrations.AddField( + model_name='offer', + name='status', + field=models.CharField(choices=[('a', 'Accepted'), ('p', 'Pending'), ('d', 'Declined')], default='p', max_length=8), + ), + migrations.AddField( + model_name='offer', + name='timestamp', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='user', + name='city', + field=models.TextField(blank=True, max_length=50), + ), + migrations.AddField( + model_name='user', + name='country', + field=models.TextField(blank=True, max_length=50), + ), + migrations.AddField( + model_name='user', + name='phone_number', + field=models.TextField(blank=True, max_length=50), + ), + migrations.AddField( + model_name='user', + name='street_address', + field=models.TextField(blank=True, max_length=50), + ), + migrations.AlterField( + model_name='offer', + name='recipient', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='received_offers', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='user', + name='coach', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='athletes', to=settings.AUTH_USER_MODEL), + ), + migrations.DeleteModel( + name='OfferResponse', + ), + migrations.AddField( + model_name='athletefile', + name='athlete', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='coach_files', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='athletefile', + name='owner', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='athlete_files', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/backend/secfit/workouts/migrations/0002_auto_20210304_2241.py b/backend/secfit/workouts/migrations/0002_auto_20210304_2241.py new file mode 100644 index 0000000000000000000000000000000000000000..739d4979daf258fd940a9720d18555f0ece5b077 --- /dev/null +++ b/backend/secfit/workouts/migrations/0002_auto_20210304_2241.py @@ -0,0 +1,54 @@ +# Generated by Django 3.1 on 2021-03-04 22:41 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('suggested_workouts', '0002_auto_20210304_2241'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('workouts', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='RememberMe', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('remember_me', models.CharField(max_length=500)), + ], + ), + migrations.AddField( + model_name='exerciseinstance', + name='suggested_workout', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='suggested_exercise_instances', to='suggested_workouts.suggestedworkout'), + ), + migrations.AddField( + model_name='workout', + name='planned', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='workoutfile', + name='suggested_workout', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='suggested_workout_files', to='suggested_workouts.suggestedworkout'), + ), + migrations.AlterField( + model_name='exerciseinstance', + name='workout', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='exercise_instances', to='workouts.workout'), + ), + migrations.AlterField( + model_name='workoutfile', + name='owner', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='workout_files', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='workoutfile', + name='workout', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='files', to='workouts.workout'), + ), + ] diff --git a/backend/secfit/workouts/views.py b/backend/secfit/workouts/views.py index 1250cf3503436a4a30f5fc8114e7eaf318773793..26254774232a1baa7737a3f36a476dd0a083f4fc 100644 --- a/backend/secfit/workouts/views.py +++ b/backend/secfit/workouts/views.py @@ -142,9 +142,7 @@ class WorkoutList( # - The workout has coach visibility and the requesting user is the owner's coach qs = Workout.objects.filter( Q(visibility="PU") - | Q(owner=self.request.user) | (Q(visibility="CO") & Q(owner__coach=self.request.user)) - | (Q(visibility= "PR") & Q(owner=self.request.user)) ).distinct() # Check if the planned workout has happened if len(qs) > 0: diff --git a/frontend/.gitignore b/frontend/.gitignore index afbcf7b942bc10475416a902e347474eedbea14e..103acd42ec68a9862973a8a7f221a9aaa56eac16 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -6,4 +6,3 @@ node_modules/ # Generated by Cordova #/plugins/ platforms/browser/www/ - diff --git a/frontend/cypress.json b/frontend/cypress.json deleted file mode 100644 index fb073120c7ac922c8de818e6545923f70053064a..0000000000000000000000000000000000000000 --- a/frontend/cypress.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "baseUrl": "http://localhost:8001" -} diff --git a/frontend/cypress/fixtures/example.json b/frontend/cypress/fixtures/example.json deleted file mode 100644 index da18d9352a17d427321962199a1fa43b8ab5cfe4..0000000000000000000000000000000000000000 --- a/frontend/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} \ No newline at end of file diff --git a/frontend/cypress/fixtures/seed.json b/frontend/cypress/fixtures/seed.json deleted file mode 100644 index 32c13669406955b9fe8d6ceae493c9eacd1fcf41..0000000000000000000000000000000000000000 --- a/frontend/cypress/fixtures/seed.json +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "model": "workouts.exercise", - "pk": 1, - "fields": { - "name": "Push-up", - "description": "A push-up (or press-up in British English) is a common calisthenics exercise beginning from the prone position.", - "unit": "reps" - } - }, - { - "model": "workouts.exercise", - "pk": 2, - "fields": { - "name": "Crunch", - "description": "The crunch is one of the most popular abdominal exercises.", - "unit": "reps" - } - }, - { - "model": "workouts.exercise", - "pk": 3, - "fields": { - "name": "Plank", - "description": "The plank is an isometric core strength exercise that involves maintaining a position similar to a push-up for the maximum possible time.", - "unit": "seconds" - } - } - ] \ No newline at end of file diff --git a/frontend/cypress/fixtures/suggestedWorkout.json b/frontend/cypress/fixtures/suggestedWorkout.json deleted file mode 100644 index 9e0a247bebea6e5275bdac003fcf435b29a071ce..0000000000000000000000000000000000000000 --- a/frontend/cypress/fixtures/suggestedWorkout.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "model": "suggested_workouts.suggestedworkout", - "pk": 1, - "fields": { - "name": "Suggested Workout Seed", - "date": null, - "notes": "heiheihei", - "coach": 1, - "athlete": 2, - "status": "p" - } - } -] \ No newline at end of file diff --git a/frontend/cypress/fixtures/users.json b/frontend/cypress/fixtures/users.json deleted file mode 100644 index d980c0b9e3757ef9af88958472383dc5e122487e..0000000000000000000000000000000000000000 --- a/frontend/cypress/fixtures/users.json +++ /dev/null @@ -1,85 +0,0 @@ -[ - { - "model": "users.user", - "pk": 1, - "fields": { - "password": "pbkdf2_sha256$216000$z0k3ideAq6Zn$mlC8XmI/+IFigImw7SGhQ+ffTutLvbtCcewgDgfmzMg=", - "last_login": null, - "is_superuser": false, - "username": "coach", - "first_name": "", - "last_name": "", - "email": "coach@hei.no", - "is_staff": false, - "is_active": true, - "date_joined": "2021-03-06T13:30:38.062Z", - "coach": null, - "phone_number": "90909", - "country": "uuu", - "city": "uuu", - "street_address": "uu", - "groups": [], - "user_permissions": [] - } - }, - { - "model": "users.user", - "pk": 2, - "fields": { - "password": "pbkdf2_sha256$216000$cDkq1l1xAvqV$C/FxUDUzeqMSpc7PuaVwImucY6cFM/Ju9CMguJTclJQ=", - "last_login": null, - "is_superuser": false, - "username": "athlete", - "first_name": "", - "last_name": "", - "email": "athlete@hei.no", - "is_staff": false, - "is_active": true, - "date_joined": "2021-03-06T13:30:56.670Z", - "coach": 1, - "phone_number": "99", - "country": "hh", - "city": "hhh", - "street_address": "h", - "groups": [], - "user_permissions": [] - } - }, - { - "model": "users.offer", - "pk": 1, - "fields": { - "owner": 1, - "recipient": 2, - "status": "d", - "timestamp": "2021-03-06T13:31:08.471Z" - } - }, - { - "model": "workouts.exercise", - "pk": 1, - "fields": { - "name": "Push-up", - "description": "A push-up (or press-up in British English) is a common calisthenics exercise beginning from the prone position.", - "unit": "reps" - } - }, - { - "model": "workouts.exercise", - "pk": 2, - "fields": { - "name": "Crunch", - "description": "The crunch is one of the most popular abdominal exercises.", - "unit": "reps" - } - }, - { - "model": "workouts.exercise", - "pk": 3, - "fields": { - "name": "Plank", - "description": "The plank is an isometric core strength exercise that involves maintaining a position similar to a push-up for the maximum possible time.", - "unit": "seconds" - } - } -] diff --git a/frontend/cypress/fixtures/workouts.json b/frontend/cypress/fixtures/workouts.json deleted file mode 100644 index 56aeb5fc4b329a5df9417c7bb0ce0fe7230104c8..0000000000000000000000000000000000000000 --- a/frontend/cypress/fixtures/workouts.json +++ /dev/null @@ -1,38 +0,0 @@ -[ - { - "model": "workouts.workout", - "pk": 1, - "fields": { - "name": "Workout Private - Coach", - "date": "2021-03-01T23:03:00Z", - "notes": "Hei", - "owner": 1, - "planned": false, - "visibility": "PR" - } - }, - { - "model": "workouts.workout", - "pk": 2, - "fields": { - "name": "Workout Visible for Coach", - "date": "2021-03-01T23:05:00Z", - "notes": "dddd", - "owner": 2, - "planned": false, - "visibility": "CO" - } - }, - { - "model": "workouts.workout", - "pk": 3, - "fields": { - "name": "Workout Public - Coach", - "date": "2021-03-06T23:09:00Z", - "notes": "dddd", - "owner": 1, - "planned": false, - "visibility": "PU" - } - } -] \ No newline at end of file diff --git a/frontend/cypress/integration/examples/actions.spec.js b/frontend/cypress/integration/examples/actions.spec.js deleted file mode 100644 index 092637998e28e31357cb95102f50b2e779db6430..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/actions.spec.js +++ /dev/null @@ -1,299 +0,0 @@ -/// <reference types="cypress" /> - -context('Actions', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/actions') - }) - - // https://on.cypress.io/interacting-with-elements - - it('.type() - type into a DOM element', () => { - // https://on.cypress.io/type - cy.get('.action-email') - .type('fake@email.com').should('have.value', 'fake@email.com') - - // .type() with special character sequences - .type('{leftarrow}{rightarrow}{uparrow}{downarrow}') - .type('{del}{selectall}{backspace}') - - // .type() with key modifiers - .type('{alt}{option}') //these are equivalent - .type('{ctrl}{control}') //these are equivalent - .type('{meta}{command}{cmd}') //these are equivalent - .type('{shift}') - - // Delay each keypress by 0.1 sec - .type('slow.typing@email.com', { delay: 100 }) - .should('have.value', 'slow.typing@email.com') - - cy.get('.action-disabled') - // Ignore error checking prior to type - // like whether the input is visible or disabled - .type('disabled error checking', { force: true }) - .should('have.value', 'disabled error checking') - }) - - it('.focus() - focus on a DOM element', () => { - // https://on.cypress.io/focus - cy.get('.action-focus').focus() - .should('have.class', 'focus') - .prev().should('have.attr', 'style', 'color: orange;') - }) - - it('.blur() - blur off a DOM element', () => { - // https://on.cypress.io/blur - cy.get('.action-blur').type('About to blur').blur() - .should('have.class', 'error') - .prev().should('have.attr', 'style', 'color: red;') - }) - - it('.clear() - clears an input or textarea element', () => { - // https://on.cypress.io/clear - cy.get('.action-clear').type('Clear this text') - .should('have.value', 'Clear this text') - .clear() - .should('have.value', '') - }) - - it('.submit() - submit a form', () => { - // https://on.cypress.io/submit - cy.get('.action-form') - .find('[type="text"]').type('HALFOFF') - - cy.get('.action-form').submit() - .next().should('contain', 'Your form has been submitted!') - }) - - it('.click() - click on a DOM element', () => { - // https://on.cypress.io/click - cy.get('.action-btn').click() - - // You can click on 9 specific positions of an element: - // ----------------------------------- - // | topLeft top topRight | - // | | - // | | - // | | - // | left center right | - // | | - // | | - // | | - // | bottomLeft bottom bottomRight | - // ----------------------------------- - - // clicking in the center of the element is the default - cy.get('#action-canvas').click() - - cy.get('#action-canvas').click('topLeft') - cy.get('#action-canvas').click('top') - cy.get('#action-canvas').click('topRight') - cy.get('#action-canvas').click('left') - cy.get('#action-canvas').click('right') - cy.get('#action-canvas').click('bottomLeft') - cy.get('#action-canvas').click('bottom') - cy.get('#action-canvas').click('bottomRight') - - // .click() accepts an x and y coordinate - // that controls where the click occurs :) - - cy.get('#action-canvas') - .click(80, 75) // click 80px on x coord and 75px on y coord - .click(170, 75) - .click(80, 165) - .click(100, 185) - .click(125, 190) - .click(150, 185) - .click(170, 165) - - // click multiple elements by passing multiple: true - cy.get('.action-labels>.label').click({ multiple: true }) - - // Ignore error checking prior to clicking - cy.get('.action-opacity>.btn').click({ force: true }) - }) - - it('.dblclick() - double click on a DOM element', () => { - // https://on.cypress.io/dblclick - - // Our app has a listener on 'dblclick' event in our 'scripts.js' - // that hides the div and shows an input on double click - cy.get('.action-div').dblclick().should('not.be.visible') - cy.get('.action-input-hidden').should('be.visible') - }) - - it('.rightclick() - right click on a DOM element', () => { - // https://on.cypress.io/rightclick - - // Our app has a listener on 'contextmenu' event in our 'scripts.js' - // that hides the div and shows an input on right click - cy.get('.rightclick-action-div').rightclick().should('not.be.visible') - cy.get('.rightclick-action-input-hidden').should('be.visible') - }) - - it('.check() - check a checkbox or radio element', () => { - // https://on.cypress.io/check - - // By default, .check() will check all - // matching checkbox or radio elements in succession, one after another - cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]') - .check().should('be.checked') - - cy.get('.action-radios [type="radio"]').not('[disabled]') - .check().should('be.checked') - - // .check() accepts a value argument - cy.get('.action-radios [type="radio"]') - .check('radio1').should('be.checked') - - // .check() accepts an array of values - cy.get('.action-multiple-checkboxes [type="checkbox"]') - .check(['checkbox1', 'checkbox2']).should('be.checked') - - // Ignore error checking prior to checking - cy.get('.action-checkboxes [disabled]') - .check({ force: true }).should('be.checked') - - cy.get('.action-radios [type="radio"]') - .check('radio3', { force: true }).should('be.checked') - }) - - it('.uncheck() - uncheck a checkbox element', () => { - // https://on.cypress.io/uncheck - - // By default, .uncheck() will uncheck all matching - // checkbox elements in succession, one after another - cy.get('.action-check [type="checkbox"]') - .not('[disabled]') - .uncheck().should('not.be.checked') - - // .uncheck() accepts a value argument - cy.get('.action-check [type="checkbox"]') - .check('checkbox1') - .uncheck('checkbox1').should('not.be.checked') - - // .uncheck() accepts an array of values - cy.get('.action-check [type="checkbox"]') - .check(['checkbox1', 'checkbox3']) - .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked') - - // Ignore error checking prior to unchecking - cy.get('.action-check [disabled]') - .uncheck({ force: true }).should('not.be.checked') - }) - - it('.select() - select an option in a <select> element', () => { - // https://on.cypress.io/select - - // at first, no option should be selected - cy.get('.action-select') - .should('have.value', '--Select a fruit--') - - // Select option(s) with matching text content - cy.get('.action-select').select('apples') - // confirm the apples were selected - // note that each value starts with "fr-" in our HTML - cy.get('.action-select').should('have.value', 'fr-apples') - - cy.get('.action-select-multiple') - .select(['apples', 'oranges', 'bananas']) - // when getting multiple values, invoke "val" method first - .invoke('val') - .should('deep.equal', ['fr-apples', 'fr-oranges', 'fr-bananas']) - - // Select option(s) with matching value - cy.get('.action-select').select('fr-bananas') - // can attach an assertion right away to the element - .should('have.value', 'fr-bananas') - - cy.get('.action-select-multiple') - .select(['fr-apples', 'fr-oranges', 'fr-bananas']) - .invoke('val') - .should('deep.equal', ['fr-apples', 'fr-oranges', 'fr-bananas']) - - // assert the selected values include oranges - cy.get('.action-select-multiple') - .invoke('val').should('include', 'fr-oranges') - }) - - it('.scrollIntoView() - scroll an element into view', () => { - // https://on.cypress.io/scrollintoview - - // normally all of these buttons are hidden, - // because they're not within - // the viewable area of their parent - // (we need to scroll to see them) - cy.get('#scroll-horizontal button') - .should('not.be.visible') - - // scroll the button into view, as if the user had scrolled - cy.get('#scroll-horizontal button').scrollIntoView() - .should('be.visible') - - cy.get('#scroll-vertical button') - .should('not.be.visible') - - // Cypress handles the scroll direction needed - cy.get('#scroll-vertical button').scrollIntoView() - .should('be.visible') - - cy.get('#scroll-both button') - .should('not.be.visible') - - // Cypress knows to scroll to the right and down - cy.get('#scroll-both button').scrollIntoView() - .should('be.visible') - }) - - it('.trigger() - trigger an event on a DOM element', () => { - // https://on.cypress.io/trigger - - // To interact with a range input (slider) - // we need to set its value & trigger the - // event to signal it changed - - // Here, we invoke jQuery's val() method to set - // the value and trigger the 'change' event - cy.get('.trigger-input-range') - .invoke('val', 25) - .trigger('change') - .get('input[type=range]').siblings('p') - .should('have.text', '25') - }) - - it('cy.scrollTo() - scroll the window or element to a position', () => { - // https://on.cypress.io/scrollto - - // You can scroll to 9 specific positions of an element: - // ----------------------------------- - // | topLeft top topRight | - // | | - // | | - // | | - // | left center right | - // | | - // | | - // | | - // | bottomLeft bottom bottomRight | - // ----------------------------------- - - // if you chain .scrollTo() off of cy, we will - // scroll the entire window - cy.scrollTo('bottom') - - cy.get('#scrollable-horizontal').scrollTo('right') - - // or you can scroll to a specific coordinate: - // (x axis, y axis) in pixels - cy.get('#scrollable-vertical').scrollTo(250, 250) - - // or you can scroll to a specific percentage - // of the (width, height) of the element - cy.get('#scrollable-both').scrollTo('75%', '25%') - - // control the easing of the scroll (default is 'swing') - cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' }) - - // control the duration of the scroll (in ms) - cy.get('#scrollable-both').scrollTo('center', { duration: 2000 }) - }) -}) diff --git a/frontend/cypress/integration/examples/aliasing.spec.js b/frontend/cypress/integration/examples/aliasing.spec.js deleted file mode 100644 index a02fb2bb93d7f292d5b72f74a73da64736a7fafe..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/aliasing.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -/// <reference types="cypress" /> - -context('Aliasing', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/aliasing') - }) - - it('.as() - alias a DOM element for later use', () => { - // https://on.cypress.io/as - - // Alias a DOM element for use later - // We don't have to traverse to the element - // later in our code, we reference it with @ - - cy.get('.as-table').find('tbody>tr') - .first().find('td').first() - .find('button').as('firstBtn') - - // when we reference the alias, we place an - // @ in front of its name - cy.get('@firstBtn').click() - - cy.get('@firstBtn') - .should('have.class', 'btn-success') - .and('contain', 'Changed') - }) - - it('.as() - alias a route for later use', () => { - // Alias the route to wait for its response - cy.intercept('GET', '**/comments/*').as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.network-btn').click() - - // https://on.cypress.io/wait - cy.wait('@getComment').its('response.statusCode').should('eq', 200) - }) -}) diff --git a/frontend/cypress/integration/examples/assertions.spec.js b/frontend/cypress/integration/examples/assertions.spec.js deleted file mode 100644 index 5ba93d1db6dd62a7851da5f118f90dda58107b93..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/assertions.spec.js +++ /dev/null @@ -1,177 +0,0 @@ -/// <reference types="cypress" /> - -context('Assertions', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/assertions') - }) - - describe('Implicit Assertions', () => { - it('.should() - make an assertion about the current subject', () => { - // https://on.cypress.io/should - cy.get('.assertion-table') - .find('tbody tr:last') - .should('have.class', 'success') - .find('td') - .first() - // checking the text of the <td> element in various ways - .should('have.text', 'Column content') - .should('contain', 'Column content') - .should('have.html', 'Column content') - // chai-jquery uses "is()" to check if element matches selector - .should('match', 'td') - // to match text content against a regular expression - // first need to invoke jQuery method text() - // and then match using regular expression - .invoke('text') - .should('match', /column content/i) - - // a better way to check element's text content against a regular expression - // is to use "cy.contains" - // https://on.cypress.io/contains - cy.get('.assertion-table') - .find('tbody tr:last') - // finds first <td> element with text content matching regular expression - .contains('td', /column content/i) - .should('be.visible') - - // for more information about asserting element's text - // see https://on.cypress.io/using-cypress-faq#How-do-I-get-an-element’s-text-contents - }) - - it('.and() - chain multiple assertions together', () => { - // https://on.cypress.io/and - cy.get('.assertions-link') - .should('have.class', 'active') - .and('have.attr', 'href') - .and('include', 'cypress.io') - }) - }) - - describe('Explicit Assertions', () => { - // https://on.cypress.io/assertions - it('expect - make an assertion about a specified subject', () => { - // We can use Chai's BDD style assertions - expect(true).to.be.true - const o = { foo: 'bar' } - - expect(o).to.equal(o) - expect(o).to.deep.equal({ foo: 'bar' }) - // matching text using regular expression - expect('FooBar').to.match(/bar$/i) - }) - - it('pass your own callback function to should()', () => { - // Pass a function to should that can have any number - // of explicit assertions within it. - // The ".should(cb)" function will be retried - // automatically until it passes all your explicit assertions or times out. - cy.get('.assertions-p') - .find('p') - .should(($p) => { - // https://on.cypress.io/$ - // return an array of texts from all of the p's - // @ts-ignore TS6133 unused variable - const texts = $p.map((i, el) => Cypress.$(el).text()) - - // jquery map returns jquery object - // and .get() convert this to simple array - const paragraphs = texts.get() - - // array should have length of 3 - expect(paragraphs, 'has 3 paragraphs').to.have.length(3) - - // use second argument to expect(...) to provide clear - // message with each assertion - expect(paragraphs, 'has expected text in each paragraph').to.deep.eq([ - 'Some text from first p', - 'More text from second p', - 'And even more text from third p', - ]) - }) - }) - - it('finds element by class name regex', () => { - cy.get('.docs-header') - .find('div') - // .should(cb) callback function will be retried - .should(($div) => { - expect($div).to.have.length(1) - - const className = $div[0].className - - expect(className).to.match(/heading-/) - }) - // .then(cb) callback is not retried, - // it either passes or fails - .then(($div) => { - expect($div, 'text content').to.have.text('Introduction') - }) - }) - - it('can throw any error', () => { - cy.get('.docs-header') - .find('div') - .should(($div) => { - if ($div.length !== 1) { - // you can throw your own errors - throw new Error('Did not find 1 element') - } - - const className = $div[0].className - - if (!className.match(/heading-/)) { - throw new Error(`Could not find class "heading-" in ${className}`) - } - }) - }) - - it('matches unknown text between two elements', () => { - /** - * Text from the first element. - * @type {string} - */ - let text - - /** - * Normalizes passed text, - * useful before comparing text with spaces and different capitalization. - * @param {string} s Text to normalize - */ - const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase() - - cy.get('.two-elements') - .find('.first') - .then(($first) => { - // save text from the first element - text = normalizeText($first.text()) - }) - - cy.get('.two-elements') - .find('.second') - .should(($div) => { - // we can massage text before comparing - const secondText = normalizeText($div.text()) - - expect(secondText, 'second text').to.equal(text) - }) - }) - - it('assert - assert shape of an object', () => { - const person = { - name: 'Joe', - age: 20, - } - - assert.isObject(person, 'value is object') - }) - - it('retries the should callback until assertions pass', () => { - cy.get('#random-number') - .should(($div) => { - const n = parseFloat($div.text()) - - expect(n).to.be.gte(1).and.be.lte(10) - }) - }) - }) -}) diff --git a/frontend/cypress/integration/examples/connectors.spec.js b/frontend/cypress/integration/examples/connectors.spec.js deleted file mode 100644 index ae8799181d1531938ae92a3d90515ea624dd4383..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/connectors.spec.js +++ /dev/null @@ -1,97 +0,0 @@ -/// <reference types="cypress" /> - -context('Connectors', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/connectors') - }) - - it('.each() - iterate over an array of elements', () => { - // https://on.cypress.io/each - cy.get('.connectors-each-ul>li') - .each(($el, index, $list) => { - console.log($el, index, $list) - }) - }) - - it('.its() - get properties on the current subject', () => { - // https://on.cypress.io/its - cy.get('.connectors-its-ul>li') - // calls the 'length' property yielding that value - .its('length') - .should('be.gt', 2) - }) - - it('.invoke() - invoke a function on the current subject', () => { - // our div is hidden in our script.js - // $('.connectors-div').hide() - - // https://on.cypress.io/invoke - cy.get('.connectors-div').should('be.hidden') - // call the jquery method 'show' on the 'div.container' - .invoke('show') - .should('be.visible') - }) - - it('.spread() - spread an array as individual args to callback function', () => { - // https://on.cypress.io/spread - const arr = ['foo', 'bar', 'baz'] - - cy.wrap(arr).spread((foo, bar, baz) => { - expect(foo).to.eq('foo') - expect(bar).to.eq('bar') - expect(baz).to.eq('baz') - }) - }) - - describe('.then()', () => { - it('invokes a callback function with the current subject', () => { - // https://on.cypress.io/then - cy.get('.connectors-list > li') - .then(($lis) => { - expect($lis, '3 items').to.have.length(3) - expect($lis.eq(0), 'first item').to.contain('Walk the dog') - expect($lis.eq(1), 'second item').to.contain('Feed the cat') - expect($lis.eq(2), 'third item').to.contain('Write JavaScript') - }) - }) - - it('yields the returned value to the next command', () => { - cy.wrap(1) - .then((num) => { - expect(num).to.equal(1) - - return 2 - }) - .then((num) => { - expect(num).to.equal(2) - }) - }) - - it('yields the original subject without return', () => { - cy.wrap(1) - .then((num) => { - expect(num).to.equal(1) - // note that nothing is returned from this callback - }) - .then((num) => { - // this callback receives the original unchanged value 1 - expect(num).to.equal(1) - }) - }) - - it('yields the value yielded by the last Cypress command inside', () => { - cy.wrap(1) - .then((num) => { - expect(num).to.equal(1) - // note how we run a Cypress command - // the result yielded by this Cypress command - // will be passed to the second ".then" - cy.wrap(2) - }) - .then((num) => { - // this callback receives the value yielded by "cy.wrap(2)" - expect(num).to.equal(2) - }) - }) - }) -}) diff --git a/frontend/cypress/integration/examples/cookies.spec.js b/frontend/cypress/integration/examples/cookies.spec.js deleted file mode 100644 index 31587ff907dea51ccb7c3e6b6293465a9a04de2d..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/cookies.spec.js +++ /dev/null @@ -1,77 +0,0 @@ -/// <reference types="cypress" /> - -context('Cookies', () => { - beforeEach(() => { - Cypress.Cookies.debug(true) - - cy.visit('https://example.cypress.io/commands/cookies') - - // clear cookies again after visiting to remove - // any 3rd party cookies picked up such as cloudflare - cy.clearCookies() - }) - - it('cy.getCookie() - get a browser cookie', () => { - // https://on.cypress.io/getcookie - cy.get('#getCookie .set-a-cookie').click() - - // cy.getCookie() yields a cookie object - cy.getCookie('token').should('have.property', 'value', '123ABC') - }) - - it('cy.getCookies() - get browser cookies', () => { - // https://on.cypress.io/getcookies - cy.getCookies().should('be.empty') - - cy.get('#getCookies .set-a-cookie').click() - - // cy.getCookies() yields an array of cookies - cy.getCookies().should('have.length', 1).should((cookies) => { - // each cookie has these properties - expect(cookies[0]).to.have.property('name', 'token') - expect(cookies[0]).to.have.property('value', '123ABC') - expect(cookies[0]).to.have.property('httpOnly', false) - expect(cookies[0]).to.have.property('secure', false) - expect(cookies[0]).to.have.property('domain') - expect(cookies[0]).to.have.property('path') - }) - }) - - it('cy.setCookie() - set a browser cookie', () => { - // https://on.cypress.io/setcookie - cy.getCookies().should('be.empty') - - cy.setCookie('foo', 'bar') - - // cy.getCookie() yields a cookie object - cy.getCookie('foo').should('have.property', 'value', 'bar') - }) - - it('cy.clearCookie() - clear a browser cookie', () => { - // https://on.cypress.io/clearcookie - cy.getCookie('token').should('be.null') - - cy.get('#clearCookie .set-a-cookie').click() - - cy.getCookie('token').should('have.property', 'value', '123ABC') - - // cy.clearCookies() yields null - cy.clearCookie('token').should('be.null') - - cy.getCookie('token').should('be.null') - }) - - it('cy.clearCookies() - clear browser cookies', () => { - // https://on.cypress.io/clearcookies - cy.getCookies().should('be.empty') - - cy.get('#clearCookies .set-a-cookie').click() - - cy.getCookies().should('have.length', 1) - - // cy.clearCookies() yields null - cy.clearCookies() - - cy.getCookies().should('be.empty') - }) -}) diff --git a/frontend/cypress/integration/examples/cypress_api.spec.js b/frontend/cypress/integration/examples/cypress_api.spec.js deleted file mode 100644 index ec8ceaeda4edef699670761f7fbf30289243c155..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/cypress_api.spec.js +++ /dev/null @@ -1,202 +0,0 @@ -/// <reference types="cypress" /> - -context('Cypress.Commands', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - // https://on.cypress.io/custom-commands - - it('.add() - create a custom command', () => { - Cypress.Commands.add('console', { - prevSubject: true, - }, (subject, method) => { - // the previous subject is automatically received - // and the commands arguments are shifted - - // allow us to change the console method used - method = method || 'log' - - // log the subject to the console - // @ts-ignore TS7017 - console[method]('The subject is', subject) - - // whatever we return becomes the new subject - // we don't want to change the subject so - // we return whatever was passed in - return subject - }) - - // @ts-ignore TS2339 - cy.get('button').console('info').then(($button) => { - // subject is still $button - }) - }) -}) - -context('Cypress.Cookies', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - // https://on.cypress.io/cookies - it('.debug() - enable or disable debugging', () => { - Cypress.Cookies.debug(true) - - // Cypress will now log in the console when - // cookies are set or cleared - cy.setCookie('fakeCookie', '123ABC') - cy.clearCookie('fakeCookie') - cy.setCookie('fakeCookie', '123ABC') - cy.clearCookie('fakeCookie') - cy.setCookie('fakeCookie', '123ABC') - }) - - it('.preserveOnce() - preserve cookies by key', () => { - // normally cookies are reset after each test - cy.getCookie('fakeCookie').should('not.be.ok') - - // preserving a cookie will not clear it when - // the next test starts - cy.setCookie('lastCookie', '789XYZ') - Cypress.Cookies.preserveOnce('lastCookie') - }) - - it('.defaults() - set defaults for all cookies', () => { - // now any cookie with the name 'session_id' will - // not be cleared before each new test runs - Cypress.Cookies.defaults({ - preserve: 'session_id', - }) - }) -}) - -context('Cypress.arch', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get CPU architecture name of underlying OS', () => { - // https://on.cypress.io/arch - expect(Cypress.arch).to.exist - }) -}) - -context('Cypress.config()', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get and set configuration options', () => { - // https://on.cypress.io/config - let myConfig = Cypress.config() - - expect(myConfig).to.have.property('animationDistanceThreshold', 5) - expect(myConfig).to.have.property('baseUrl', null) - expect(myConfig).to.have.property('defaultCommandTimeout', 4000) - expect(myConfig).to.have.property('requestTimeout', 5000) - expect(myConfig).to.have.property('responseTimeout', 30000) - expect(myConfig).to.have.property('viewportHeight', 660) - expect(myConfig).to.have.property('viewportWidth', 1000) - expect(myConfig).to.have.property('pageLoadTimeout', 60000) - expect(myConfig).to.have.property('waitForAnimations', true) - - expect(Cypress.config('pageLoadTimeout')).to.eq(60000) - - // this will change the config for the rest of your tests! - Cypress.config('pageLoadTimeout', 20000) - - expect(Cypress.config('pageLoadTimeout')).to.eq(20000) - - Cypress.config('pageLoadTimeout', 60000) - }) -}) - -context('Cypress.dom', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - // https://on.cypress.io/dom - it('.isHidden() - determine if a DOM element is hidden', () => { - let hiddenP = Cypress.$('.dom-p p.hidden').get(0) - let visibleP = Cypress.$('.dom-p p.visible').get(0) - - // our first paragraph has css class 'hidden' - expect(Cypress.dom.isHidden(hiddenP)).to.be.true - expect(Cypress.dom.isHidden(visibleP)).to.be.false - }) -}) - -context('Cypress.env()', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - // We can set environment variables for highly dynamic values - - // https://on.cypress.io/environment-variables - it('Get environment variables', () => { - // https://on.cypress.io/env - // set multiple environment variables - Cypress.env({ - host: 'veronica.dev.local', - api_server: 'http://localhost:8888/v1/', - }) - - // get environment variable - expect(Cypress.env('host')).to.eq('veronica.dev.local') - - // set environment variable - Cypress.env('api_server', 'http://localhost:8888/v2/') - expect(Cypress.env('api_server')).to.eq('http://localhost:8888/v2/') - - // get all environment variable - expect(Cypress.env()).to.have.property('host', 'veronica.dev.local') - expect(Cypress.env()).to.have.property('api_server', 'http://localhost:8888/v2/') - }) -}) - -context('Cypress.log', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Control what is printed to the Command Log', () => { - // https://on.cypress.io/cypress-log - }) -}) - -context('Cypress.platform', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get underlying OS name', () => { - // https://on.cypress.io/platform - expect(Cypress.platform).to.be.exist - }) -}) - -context('Cypress.version', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get current version of Cypress being run', () => { - // https://on.cypress.io/version - expect(Cypress.version).to.be.exist - }) -}) - -context('Cypress.spec', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) - - it('Get current spec information', () => { - // https://on.cypress.io/spec - // wrap the object so we can inspect it easily by clicking in the command log - cy.wrap(Cypress.spec).should('include.keys', ['name', 'relative', 'absolute']) - }) -}) diff --git a/frontend/cypress/integration/examples/files.spec.js b/frontend/cypress/integration/examples/files.spec.js deleted file mode 100644 index 01a53235ee304a4b356678a90aec285830b5adb8..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/files.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -/// <reference types="cypress" /> - -/// JSON fixture file can be loaded directly using -// the built-in JavaScript bundler -// @ts-ignore -const requiredExample = require('../../fixtures/example') - -context('Files', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/files') - }) - - beforeEach(() => { - // load example.json fixture file and store - // in the test context object - cy.fixture('example.json').as('example') - }) - - it('cy.fixture() - load a fixture', () => { - // https://on.cypress.io/fixture - - // Instead of writing a response inline you can - // use a fixture file's content. - - // when application makes an Ajax request matching "GET **/comments/*" - // Cypress will intercept it and reply with the object in `example.json` fixture - cy.intercept('GET', '**/comments/*', { fixture: 'example.json' }).as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.fixture-btn').click() - - cy.wait('@getComment').its('response.body') - .should('have.property', 'name') - .and('include', 'Using fixtures to represent data') - }) - - it('cy.fixture() or require - load a fixture', function () { - // we are inside the "function () { ... }" - // callback and can use test context object "this" - // "this.example" was loaded in "beforeEach" function callback - expect(this.example, 'fixture in the test context') - .to.deep.equal(requiredExample) - - // or use "cy.wrap" and "should('deep.equal', ...)" assertion - // @ts-ignore - cy.wrap(this.example, 'fixture vs require') - .should('deep.equal', requiredExample) - }) - - it('cy.readFile() - read file contents', () => { - // https://on.cypress.io/readfile - - // You can read a file and yield its contents - // The filePath is relative to your project's root. - cy.readFile('cypress.json').then((json) => { - expect(json).to.be.an('object') - }) - }) - - it('cy.writeFile() - write to a file', () => { - // https://on.cypress.io/writefile - - // You can write to a file - - // Use a response from a request to automatically - // generate a fixture file for use later - cy.request('https://jsonplaceholder.cypress.io/users') - .then((response) => { - cy.writeFile('cypress/fixtures/users.json', response.body) - }) - - cy.fixture('users').should((users) => { - expect(users[0].name).to.exist - }) - - // JavaScript arrays and objects are stringified - // and formatted into text. - cy.writeFile('cypress/fixtures/profile.json', { - id: 8739, - name: 'Jane', - email: 'jane@example.com', - }) - - cy.fixture('profile').should((profile) => { - expect(profile.name).to.eq('Jane') - }) - }) -}) diff --git a/frontend/cypress/integration/examples/local_storage.spec.js b/frontend/cypress/integration/examples/local_storage.spec.js deleted file mode 100644 index 5f83b8de73d7cfaa7e3f94ef6989a27e2828fac3..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/local_storage.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/// <reference types="cypress" /> - -context('Local Storage', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/local-storage') - }) - // Although local storage is automatically cleared - // in between tests to maintain a clean state - // sometimes we need to clear the local storage manually - - it('cy.clearLocalStorage() - clear all data in local storage', () => { - // https://on.cypress.io/clearlocalstorage - cy.get('.ls-btn').click().should(() => { - expect(localStorage.getItem('prop1')).to.eq('red') - expect(localStorage.getItem('prop2')).to.eq('blue') - expect(localStorage.getItem('prop3')).to.eq('magenta') - }) - - // clearLocalStorage() yields the localStorage object - cy.clearLocalStorage().should((ls) => { - expect(ls.getItem('prop1')).to.be.null - expect(ls.getItem('prop2')).to.be.null - expect(ls.getItem('prop3')).to.be.null - }) - - // Clear key matching string in Local Storage - cy.get('.ls-btn').click().should(() => { - expect(localStorage.getItem('prop1')).to.eq('red') - expect(localStorage.getItem('prop2')).to.eq('blue') - expect(localStorage.getItem('prop3')).to.eq('magenta') - }) - - cy.clearLocalStorage('prop1').should((ls) => { - expect(ls.getItem('prop1')).to.be.null - expect(ls.getItem('prop2')).to.eq('blue') - expect(ls.getItem('prop3')).to.eq('magenta') - }) - - // Clear keys matching regex in Local Storage - cy.get('.ls-btn').click().should(() => { - expect(localStorage.getItem('prop1')).to.eq('red') - expect(localStorage.getItem('prop2')).to.eq('blue') - expect(localStorage.getItem('prop3')).to.eq('magenta') - }) - - cy.clearLocalStorage(/prop1|2/).should((ls) => { - expect(ls.getItem('prop1')).to.be.null - expect(ls.getItem('prop2')).to.be.null - expect(ls.getItem('prop3')).to.eq('magenta') - }) - }) -}) diff --git a/frontend/cypress/integration/examples/location.spec.js b/frontend/cypress/integration/examples/location.spec.js deleted file mode 100644 index 299867da07ef0e82bd055098f01558d9e4230fa4..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/location.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/// <reference types="cypress" /> - -context('Location', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/location') - }) - - it('cy.hash() - get the current URL hash', () => { - // https://on.cypress.io/hash - cy.hash().should('be.empty') - }) - - it('cy.location() - get window.location', () => { - // https://on.cypress.io/location - cy.location().should((location) => { - expect(location.hash).to.be.empty - expect(location.href).to.eq('https://example.cypress.io/commands/location') - expect(location.host).to.eq('example.cypress.io') - expect(location.hostname).to.eq('example.cypress.io') - expect(location.origin).to.eq('https://example.cypress.io') - expect(location.pathname).to.eq('/commands/location') - expect(location.port).to.eq('') - expect(location.protocol).to.eq('https:') - expect(location.search).to.be.empty - }) - }) - - it('cy.url() - get the current URL', () => { - // https://on.cypress.io/url - cy.url().should('eq', 'https://example.cypress.io/commands/location') - }) -}) diff --git a/frontend/cypress/integration/examples/misc.spec.js b/frontend/cypress/integration/examples/misc.spec.js deleted file mode 100644 index 7222bf4bd1c99cdafaed24057497182737c3fd78..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/misc.spec.js +++ /dev/null @@ -1,104 +0,0 @@ -/// <reference types="cypress" /> - -context('Misc', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/misc') - }) - - it('.end() - end the command chain', () => { - // https://on.cypress.io/end - - // cy.end is useful when you want to end a chain of commands - // and force Cypress to re-query from the root element - cy.get('.misc-table').within(() => { - // ends the current chain and yields null - cy.contains('Cheryl').click().end() - - // queries the entire table again - cy.contains('Charles').click() - }) - }) - - it('cy.exec() - execute a system command', () => { - // execute a system command. - // so you can take actions necessary for - // your test outside the scope of Cypress. - // https://on.cypress.io/exec - - // we can use Cypress.platform string to - // select appropriate command - // https://on.cypress/io/platform - cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`) - - // on CircleCI Windows build machines we have a failure to run bash shell - // https://github.com/cypress-io/cypress/issues/5169 - // so skip some of the tests by passing flag "--env circle=true" - const isCircleOnWindows = Cypress.platform === 'win32' && Cypress.env('circle') - - if (isCircleOnWindows) { - cy.log('Skipping test on CircleCI') - - return - } - - // cy.exec problem on Shippable CI - // https://github.com/cypress-io/cypress/issues/6718 - const isShippable = Cypress.platform === 'linux' && Cypress.env('shippable') - - if (isShippable) { - cy.log('Skipping test on ShippableCI') - - return - } - - cy.exec('echo Jane Lane') - .its('stdout').should('contain', 'Jane Lane') - - if (Cypress.platform === 'win32') { - cy.exec('print cypress.json') - .its('stderr').should('be.empty') - } else { - cy.exec('cat cypress.json') - .its('stderr').should('be.empty') - - cy.exec('pwd') - .its('code').should('eq', 0) - } - }) - - it('cy.focused() - get the DOM element that has focus', () => { - // https://on.cypress.io/focused - cy.get('.misc-form').find('#name').click() - cy.focused().should('have.id', 'name') - - cy.get('.misc-form').find('#description').click() - cy.focused().should('have.id', 'description') - }) - - context('Cypress.Screenshot', function () { - it('cy.screenshot() - take a screenshot', () => { - // https://on.cypress.io/screenshot - cy.screenshot('my-image') - }) - - it('Cypress.Screenshot.defaults() - change default config of screenshots', function () { - Cypress.Screenshot.defaults({ - blackout: ['.foo'], - capture: 'viewport', - clip: { x: 0, y: 0, width: 200, height: 200 }, - scale: false, - disableTimersAndAnimations: true, - screenshotOnRunFailure: true, - onBeforeScreenshot () { }, - onAfterScreenshot () { }, - }) - }) - }) - - it('cy.wrap() - wrap an object', () => { - // https://on.cypress.io/wrap - cy.wrap({ foo: 'bar' }) - .should('have.property', 'foo') - .and('include', 'bar') - }) -}) diff --git a/frontend/cypress/integration/examples/navigation.spec.js b/frontend/cypress/integration/examples/navigation.spec.js deleted file mode 100644 index b85a46890c8f946ac3d9b28bb4da4dbe56ea1a89..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/navigation.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -/// <reference types="cypress" /> - -context('Navigation', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io') - cy.get('.navbar-nav').contains('Commands').click() - cy.get('.dropdown-menu').contains('Navigation').click() - }) - - it('cy.go() - go back or forward in the browser\'s history', () => { - // https://on.cypress.io/go - - cy.location('pathname').should('include', 'navigation') - - cy.go('back') - cy.location('pathname').should('not.include', 'navigation') - - cy.go('forward') - cy.location('pathname').should('include', 'navigation') - - // clicking back - cy.go(-1) - cy.location('pathname').should('not.include', 'navigation') - - // clicking forward - cy.go(1) - cy.location('pathname').should('include', 'navigation') - }) - - it('cy.reload() - reload the page', () => { - // https://on.cypress.io/reload - cy.reload() - - // reload the page without using the cache - cy.reload(true) - }) - - it('cy.visit() - visit a remote url', () => { - // https://on.cypress.io/visit - - // Visit any sub-domain of your current domain - - // Pass options to the visit - cy.visit('https://example.cypress.io/commands/navigation', { - timeout: 50000, // increase total time for the visit to resolve - onBeforeLoad (contentWindow) { - // contentWindow is the remote page's window object - expect(typeof contentWindow === 'object').to.be.true - }, - onLoad (contentWindow) { - // contentWindow is the remote page's window object - expect(typeof contentWindow === 'object').to.be.true - }, - }) - }) -}) diff --git a/frontend/cypress/integration/examples/network_requests.spec.js b/frontend/cypress/integration/examples/network_requests.spec.js deleted file mode 100644 index 11213a0e852a4e3515b8b508558f1aea652be904..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/network_requests.spec.js +++ /dev/null @@ -1,163 +0,0 @@ -/// <reference types="cypress" /> - -context('Network Requests', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/network-requests') - }) - - // Manage HTTP requests in your app - - it('cy.request() - make an XHR request', () => { - // https://on.cypress.io/request - cy.request('https://jsonplaceholder.cypress.io/comments') - .should((response) => { - expect(response.status).to.eq(200) - // the server sometimes gets an extra comment posted from another machine - // which gets returned as 1 extra object - expect(response.body).to.have.property('length').and.be.oneOf([500, 501]) - expect(response).to.have.property('headers') - expect(response).to.have.property('duration') - }) - }) - - it('cy.request() - verify response using BDD syntax', () => { - cy.request('https://jsonplaceholder.cypress.io/comments') - .then((response) => { - // https://on.cypress.io/assertions - expect(response).property('status').to.equal(200) - expect(response).property('body').to.have.property('length').and.be.oneOf([500, 501]) - expect(response).to.include.keys('headers', 'duration') - }) - }) - - it('cy.request() with query parameters', () => { - // will execute request - // https://jsonplaceholder.cypress.io/comments?postId=1&id=3 - cy.request({ - url: 'https://jsonplaceholder.cypress.io/comments', - qs: { - postId: 1, - id: 3, - }, - }) - .its('body') - .should('be.an', 'array') - .and('have.length', 1) - .its('0') // yields first element of the array - .should('contain', { - postId: 1, - id: 3, - }) - }) - - it('cy.request() - pass result to the second request', () => { - // first, let's find out the userId of the first user we have - cy.request('https://jsonplaceholder.cypress.io/users?_limit=1') - .its('body') // yields the response object - .its('0') // yields the first element of the returned list - // the above two commands its('body').its('0') - // can be written as its('body.0') - // if you do not care about TypeScript checks - .then((user) => { - expect(user).property('id').to.be.a('number') - // make a new post on behalf of the user - cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', { - userId: user.id, - title: 'Cypress Test Runner', - body: 'Fast, easy and reliable testing for anything that runs in a browser.', - }) - }) - // note that the value here is the returned value of the 2nd request - // which is the new post object - .then((response) => { - expect(response).property('status').to.equal(201) // new entity created - expect(response).property('body').to.contain({ - title: 'Cypress Test Runner', - }) - - // we don't know the exact post id - only that it will be > 100 - // since JSONPlaceholder has built-in 100 posts - expect(response.body).property('id').to.be.a('number') - .and.to.be.gt(100) - - // we don't know the user id here - since it was in above closure - // so in this test just confirm that the property is there - expect(response.body).property('userId').to.be.a('number') - }) - }) - - it('cy.request() - save response in the shared test context', () => { - // https://on.cypress.io/variables-and-aliases - cy.request('https://jsonplaceholder.cypress.io/users?_limit=1') - .its('body').its('0') // yields the first element of the returned list - .as('user') // saves the object in the test context - .then(function () { - // NOTE 👀 - // By the time this callback runs the "as('user')" command - // has saved the user object in the test context. - // To access the test context we need to use - // the "function () { ... }" callback form, - // otherwise "this" points at a wrong or undefined object! - cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', { - userId: this.user.id, - title: 'Cypress Test Runner', - body: 'Fast, easy and reliable testing for anything that runs in a browser.', - }) - .its('body').as('post') // save the new post from the response - }) - .then(function () { - // When this callback runs, both "cy.request" API commands have finished - // and the test context has "user" and "post" objects set. - // Let's verify them. - expect(this.post, 'post has the right user id').property('userId').to.equal(this.user.id) - }) - }) - - it('cy.intercept() - route responses to matching requests', () => { - // https://on.cypress.io/intercept - - let message = 'whoa, this comment does not exist' - - // Listen to GET to comments/1 - cy.intercept('GET', '**/comments/*').as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.network-btn').click() - - // https://on.cypress.io/wait - cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304]) - - // Listen to POST to comments - cy.intercept('POST', '**/comments').as('postComment') - - // we have code that posts a comment when - // the button is clicked in scripts.js - cy.get('.network-post').click() - cy.wait('@postComment').should(({ request, response }) => { - expect(request.body).to.include('email') - expect(request.headers).to.have.property('content-type') - expect(response && response.body).to.have.property('name', 'Using POST in cy.intercept()') - }) - - // Stub a response to PUT comments/ **** - cy.intercept({ - method: 'PUT', - url: '**/comments/*', - }, { - statusCode: 404, - body: { error: message }, - headers: { 'access-control-allow-origin': '*' }, - delayMs: 500, - }).as('putComment') - - // we have code that puts a comment when - // the button is clicked in scripts.js - cy.get('.network-put').click() - - cy.wait('@putComment') - - // our 404 statusCode logic in scripts.js executed - cy.get('.network-put-comment').should('contain', message) - }) -}) diff --git a/frontend/cypress/integration/examples/querying.spec.js b/frontend/cypress/integration/examples/querying.spec.js deleted file mode 100644 index 00970480f6c8641dd2ee12a2afb417e720417454..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/querying.spec.js +++ /dev/null @@ -1,114 +0,0 @@ -/// <reference types="cypress" /> - -context('Querying', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/querying') - }) - - // The most commonly used query is 'cy.get()', you can - // think of this like the '$' in jQuery - - it('cy.get() - query DOM elements', () => { - // https://on.cypress.io/get - - cy.get('#query-btn').should('contain', 'Button') - - cy.get('.query-btn').should('contain', 'Button') - - cy.get('#querying .well>button:first').should('contain', 'Button') - // ↲ - // Use CSS selectors just like jQuery - - cy.get('[data-test-id="test-example"]').should('have.class', 'example') - - // 'cy.get()' yields jQuery object, you can get its attribute - // by invoking `.attr()` method - cy.get('[data-test-id="test-example"]') - .invoke('attr', 'data-test-id') - .should('equal', 'test-example') - - // or you can get element's CSS property - cy.get('[data-test-id="test-example"]') - .invoke('css', 'position') - .should('equal', 'static') - - // or use assertions directly during 'cy.get()' - // https://on.cypress.io/assertions - cy.get('[data-test-id="test-example"]') - .should('have.attr', 'data-test-id', 'test-example') - .and('have.css', 'position', 'static') - }) - - it('cy.contains() - query DOM elements with matching content', () => { - // https://on.cypress.io/contains - cy.get('.query-list') - .contains('bananas') - .should('have.class', 'third') - - // we can pass a regexp to `.contains()` - cy.get('.query-list') - .contains(/^b\w+/) - .should('have.class', 'third') - - cy.get('.query-list') - .contains('apples') - .should('have.class', 'first') - - // passing a selector to contains will - // yield the selector containing the text - cy.get('#querying') - .contains('ul', 'oranges') - .should('have.class', 'query-list') - - cy.get('.query-button') - .contains('Save Form') - .should('have.class', 'btn') - }) - - it('.within() - query DOM elements within a specific element', () => { - // https://on.cypress.io/within - cy.get('.query-form').within(() => { - cy.get('input:first').should('have.attr', 'placeholder', 'Email') - cy.get('input:last').should('have.attr', 'placeholder', 'Password') - }) - }) - - it('cy.root() - query the root DOM element', () => { - // https://on.cypress.io/root - - // By default, root is the document - cy.root().should('match', 'html') - - cy.get('.query-ul').within(() => { - // In this within, the root is now the ul DOM element - cy.root().should('have.class', 'query-ul') - }) - }) - - it('best practices - selecting elements', () => { - // https://on.cypress.io/best-practices#Selecting-Elements - cy.get('[data-cy=best-practices-selecting-elements]').within(() => { - // Worst - too generic, no context - cy.get('button').click() - - // Bad. Coupled to styling. Highly subject to change. - cy.get('.btn.btn-large').click() - - // Average. Coupled to the `name` attribute which has HTML semantics. - cy.get('[name=submission]').click() - - // Better. But still coupled to styling or JS event listeners. - cy.get('#main').click() - - // Slightly better. Uses an ID but also ensures the element - // has an ARIA role attribute - cy.get('#main[role=button]').click() - - // Much better. But still coupled to text content that may change. - cy.contains('Submit').click() - - // Best. Insulated from all changes. - cy.get('[data-cy=submit]').click() - }) - }) -}) diff --git a/frontend/cypress/integration/examples/spies_stubs_clocks.spec.js b/frontend/cypress/integration/examples/spies_stubs_clocks.spec.js deleted file mode 100644 index 18b643ecd5a50b66edf26303a5307df98e918c17..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/spies_stubs_clocks.spec.js +++ /dev/null @@ -1,205 +0,0 @@ -/// <reference types="cypress" /> -// remove no check once Cypress.sinon is typed -// https://github.com/cypress-io/cypress/issues/6720 - -context('Spies, Stubs, and Clock', () => { - it('cy.spy() - wrap a method in a spy', () => { - // https://on.cypress.io/spy - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - - const obj = { - foo () {}, - } - - const spy = cy.spy(obj, 'foo').as('anyArgs') - - obj.foo() - - expect(spy).to.be.called - }) - - it('cy.spy() retries until assertions pass', () => { - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - - const obj = { - /** - * Prints the argument passed - * @param x {any} - */ - foo (x) { - console.log('obj.foo called with', x) - }, - } - - cy.spy(obj, 'foo').as('foo') - - setTimeout(() => { - obj.foo('first') - }, 500) - - setTimeout(() => { - obj.foo('second') - }, 2500) - - cy.get('@foo').should('have.been.calledTwice') - }) - - it('cy.stub() - create a stub and/or replace a function with stub', () => { - // https://on.cypress.io/stub - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - - const obj = { - /** - * prints both arguments to the console - * @param a {string} - * @param b {string} - */ - foo (a, b) { - console.log('a', a, 'b', b) - }, - } - - const stub = cy.stub(obj, 'foo').as('foo') - - obj.foo('foo', 'bar') - - expect(stub).to.be.called - }) - - it('cy.clock() - control time in the browser', () => { - // https://on.cypress.io/clock - - // create the date in UTC so its always the same - // no matter what local timezone the browser is running in - const now = new Date(Date.UTC(2017, 2, 14)).getTime() - - cy.clock(now) - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - cy.get('#clock-div').click() - .should('have.text', '1489449600') - }) - - it('cy.tick() - move time in the browser', () => { - // https://on.cypress.io/tick - - // create the date in UTC so its always the same - // no matter what local timezone the browser is running in - const now = new Date(Date.UTC(2017, 2, 14)).getTime() - - cy.clock(now) - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') - cy.get('#tick-div').click() - .should('have.text', '1489449600') - - cy.tick(10000) // 10 seconds passed - cy.get('#tick-div').click() - .should('have.text', '1489449610') - }) - - it('cy.stub() matches depending on arguments', () => { - // see all possible matchers at - // https://sinonjs.org/releases/latest/matchers/ - const greeter = { - /** - * Greets a person - * @param {string} name - */ - greet (name) { - return `Hello, ${name}!` - }, - } - - cy.stub(greeter, 'greet') - .callThrough() // if you want non-matched calls to call the real method - .withArgs(Cypress.sinon.match.string).returns('Hi') - .withArgs(Cypress.sinon.match.number).throws(new Error('Invalid name')) - - expect(greeter.greet('World')).to.equal('Hi') - // @ts-ignore - expect(() => greeter.greet(42)).to.throw('Invalid name') - expect(greeter.greet).to.have.been.calledTwice - - // non-matched calls goes the actual method - // @ts-ignore - expect(greeter.greet()).to.equal('Hello, undefined!') - }) - - it('matches call arguments using Sinon matchers', () => { - // see all possible matchers at - // https://sinonjs.org/releases/latest/matchers/ - const calculator = { - /** - * returns the sum of two arguments - * @param a {number} - * @param b {number} - */ - add (a, b) { - return a + b - }, - } - - const spy = cy.spy(calculator, 'add').as('add') - - expect(calculator.add(2, 3)).to.equal(5) - - // if we want to assert the exact values used during the call - expect(spy).to.be.calledWith(2, 3) - - // let's confirm "add" method was called with two numbers - expect(spy).to.be.calledWith(Cypress.sinon.match.number, Cypress.sinon.match.number) - - // alternatively, provide the value to match - expect(spy).to.be.calledWith(Cypress.sinon.match(2), Cypress.sinon.match(3)) - - // match any value - expect(spy).to.be.calledWith(Cypress.sinon.match.any, 3) - - // match any value from a list - expect(spy).to.be.calledWith(Cypress.sinon.match.in([1, 2, 3]), 3) - - /** - * Returns true if the given number is event - * @param {number} x - */ - const isEven = (x) => x % 2 === 0 - - // expect the value to pass a custom predicate function - // the second argument to "sinon.match(predicate, message)" is - // shown if the predicate does not pass and assertion fails - expect(spy).to.be.calledWith(Cypress.sinon.match(isEven, 'isEven'), 3) - - /** - * Returns a function that checks if a given number is larger than the limit - * @param {number} limit - * @returns {(x: number) => boolean} - */ - const isGreaterThan = (limit) => (x) => x > limit - - /** - * Returns a function that checks if a given number is less than the limit - * @param {number} limit - * @returns {(x: number) => boolean} - */ - const isLessThan = (limit) => (x) => x < limit - - // you can combine several matchers using "and", "or" - expect(spy).to.be.calledWith( - Cypress.sinon.match.number, - Cypress.sinon.match(isGreaterThan(2), '> 2').and(Cypress.sinon.match(isLessThan(4), '< 4')), - ) - - expect(spy).to.be.calledWith( - Cypress.sinon.match.number, - Cypress.sinon.match(isGreaterThan(200), '> 200').or(Cypress.sinon.match(3)), - ) - - // matchers can be used from BDD assertions - cy.get('@add').should('have.been.calledWith', - Cypress.sinon.match.number, Cypress.sinon.match(3)) - - // you can alias matchers for shorter test code - const { match: M } = Cypress.sinon - - cy.get('@add').should('have.been.calledWith', M.number, M(3)) - }) -}) diff --git a/frontend/cypress/integration/examples/traversal.spec.js b/frontend/cypress/integration/examples/traversal.spec.js deleted file mode 100644 index 0a3b9d33062dacdd49377d462641853ebdf3ba08..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/traversal.spec.js +++ /dev/null @@ -1,121 +0,0 @@ -/// <reference types="cypress" /> - -context('Traversal', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/traversal') - }) - - it('.children() - get child DOM elements', () => { - // https://on.cypress.io/children - cy.get('.traversal-breadcrumb') - .children('.active') - .should('contain', 'Data') - }) - - it('.closest() - get closest ancestor DOM element', () => { - // https://on.cypress.io/closest - cy.get('.traversal-badge') - .closest('ul') - .should('have.class', 'list-group') - }) - - it('.eq() - get a DOM element at a specific index', () => { - // https://on.cypress.io/eq - cy.get('.traversal-list>li') - .eq(1).should('contain', 'siamese') - }) - - it('.filter() - get DOM elements that match the selector', () => { - // https://on.cypress.io/filter - cy.get('.traversal-nav>li') - .filter('.active').should('contain', 'About') - }) - - it('.find() - get descendant DOM elements of the selector', () => { - // https://on.cypress.io/find - cy.get('.traversal-pagination') - .find('li').find('a') - .should('have.length', 7) - }) - - it('.first() - get first DOM element', () => { - // https://on.cypress.io/first - cy.get('.traversal-table td') - .first().should('contain', '1') - }) - - it('.last() - get last DOM element', () => { - // https://on.cypress.io/last - cy.get('.traversal-buttons .btn') - .last().should('contain', 'Submit') - }) - - it('.next() - get next sibling DOM element', () => { - // https://on.cypress.io/next - cy.get('.traversal-ul') - .contains('apples').next().should('contain', 'oranges') - }) - - it('.nextAll() - get all next sibling DOM elements', () => { - // https://on.cypress.io/nextall - cy.get('.traversal-next-all') - .contains('oranges') - .nextAll().should('have.length', 3) - }) - - it('.nextUntil() - get next sibling DOM elements until next el', () => { - // https://on.cypress.io/nextuntil - cy.get('#veggies') - .nextUntil('#nuts').should('have.length', 3) - }) - - it('.not() - remove DOM elements from set of DOM elements', () => { - // https://on.cypress.io/not - cy.get('.traversal-disabled .btn') - .not('[disabled]').should('not.contain', 'Disabled') - }) - - it('.parent() - get parent DOM element from DOM elements', () => { - // https://on.cypress.io/parent - cy.get('.traversal-mark') - .parent().should('contain', 'Morbi leo risus') - }) - - it('.parents() - get parent DOM elements from DOM elements', () => { - // https://on.cypress.io/parents - cy.get('.traversal-cite') - .parents().should('match', 'blockquote') - }) - - it('.parentsUntil() - get parent DOM elements from DOM elements until el', () => { - // https://on.cypress.io/parentsuntil - cy.get('.clothes-nav') - .find('.active') - .parentsUntil('.clothes-nav') - .should('have.length', 2) - }) - - it('.prev() - get previous sibling DOM element', () => { - // https://on.cypress.io/prev - cy.get('.birds').find('.active') - .prev().should('contain', 'Lorikeets') - }) - - it('.prevAll() - get all previous sibling DOM elements', () => { - // https://on.cypress.io/prevall - cy.get('.fruits-list').find('.third') - .prevAll().should('have.length', 2) - }) - - it('.prevUntil() - get all previous sibling DOM elements until el', () => { - // https://on.cypress.io/prevuntil - cy.get('.foods-list').find('#nuts') - .prevUntil('#veggies').should('have.length', 3) - }) - - it('.siblings() - get all sibling DOM elements', () => { - // https://on.cypress.io/siblings - cy.get('.traversal-pills .active') - .siblings().should('have.length', 2) - }) -}) diff --git a/frontend/cypress/integration/examples/utilities.spec.js b/frontend/cypress/integration/examples/utilities.spec.js deleted file mode 100644 index 24e61a6a7c4d1436f66236883603a5ddd16d6597..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/utilities.spec.js +++ /dev/null @@ -1,110 +0,0 @@ -/// <reference types="cypress" /> - -context('Utilities', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/utilities') - }) - - it('Cypress._ - call a lodash method', () => { - // https://on.cypress.io/_ - cy.request('https://jsonplaceholder.cypress.io/users') - .then((response) => { - let ids = Cypress._.chain(response.body).map('id').take(3).value() - - expect(ids).to.deep.eq([1, 2, 3]) - }) - }) - - it('Cypress.$ - call a jQuery method', () => { - // https://on.cypress.io/$ - let $li = Cypress.$('.utility-jquery li:first') - - cy.wrap($li) - .should('not.have.class', 'active') - .click() - .should('have.class', 'active') - }) - - it('Cypress.Blob - blob utilities and base64 string conversion', () => { - // https://on.cypress.io/blob - cy.get('.utility-blob').then(($div) => { - // https://github.com/nolanlawson/blob-util#imgSrcToDataURL - // get the dataUrl string for the javascript-logo - return Cypress.Blob.imgSrcToDataURL('https://example.cypress.io/assets/img/javascript-logo.png', undefined, 'anonymous') - .then((dataUrl) => { - // create an <img> element and set its src to the dataUrl - let img = Cypress.$('<img />', { src: dataUrl }) - - // need to explicitly return cy here since we are initially returning - // the Cypress.Blob.imgSrcToDataURL promise to our test - // append the image - $div.append(img) - - cy.get('.utility-blob img').click() - .should('have.attr', 'src', dataUrl) - }) - }) - }) - - it('Cypress.minimatch - test out glob patterns against strings', () => { - // https://on.cypress.io/minimatch - let matching = Cypress.minimatch('/users/1/comments', '/users/*/comments', { - matchBase: true, - }) - - expect(matching, 'matching wildcard').to.be.true - - matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', { - matchBase: true, - }) - - expect(matching, 'comments').to.be.false - - // ** matches against all downstream path segments - matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/**', { - matchBase: true, - }) - - expect(matching, 'comments').to.be.true - - // whereas * matches only the next path segment - - matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/*', { - matchBase: false, - }) - - expect(matching, 'comments').to.be.false - }) - - it('Cypress.Promise - instantiate a bluebird promise', () => { - // https://on.cypress.io/promise - let waited = false - - /** - * @return Bluebird<string> - */ - function waitOneSecond () { - // return a promise that resolves after 1 second - // @ts-ignore TS2351 (new Cypress.Promise) - return new Cypress.Promise((resolve, reject) => { - setTimeout(() => { - // set waited to true - waited = true - - // resolve with 'foo' string - resolve('foo') - }, 1000) - }) - } - - cy.then(() => { - // return a promise to cy.then() that - // is awaited until it resolves - // @ts-ignore TS7006 - return waitOneSecond().then((str) => { - expect(str).to.eq('foo') - expect(waited).to.be.true - }) - }) - }) -}) diff --git a/frontend/cypress/integration/examples/viewport.spec.js b/frontend/cypress/integration/examples/viewport.spec.js deleted file mode 100644 index dbcd7eeddd01e0c8f6b79ed2e8ebe69f67ce666d..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/viewport.spec.js +++ /dev/null @@ -1,59 +0,0 @@ -/// <reference types="cypress" /> - -context('Viewport', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/viewport') - }) - - it('cy.viewport() - set the viewport size and dimension', () => { - // https://on.cypress.io/viewport - - cy.get('#navbar').should('be.visible') - cy.viewport(320, 480) - - // the navbar should have collapse since our screen is smaller - cy.get('#navbar').should('not.be.visible') - cy.get('.navbar-toggle').should('be.visible').click() - cy.get('.nav').find('a').should('be.visible') - - // lets see what our app looks like on a super large screen - cy.viewport(2999, 2999) - - // cy.viewport() accepts a set of preset sizes - // to easily set the screen to a device's width and height - - // We added a cy.wait() between each viewport change so you can see - // the change otherwise it is a little too fast to see :) - - cy.viewport('macbook-15') - cy.wait(200) - cy.viewport('macbook-13') - cy.wait(200) - cy.viewport('macbook-11') - cy.wait(200) - cy.viewport('ipad-2') - cy.wait(200) - cy.viewport('ipad-mini') - cy.wait(200) - cy.viewport('iphone-6+') - cy.wait(200) - cy.viewport('iphone-6') - cy.wait(200) - cy.viewport('iphone-5') - cy.wait(200) - cy.viewport('iphone-4') - cy.wait(200) - cy.viewport('iphone-3') - cy.wait(200) - - // cy.viewport() accepts an orientation for all presets - // the default orientation is 'portrait' - cy.viewport('ipad-2', 'portrait') - cy.wait(200) - cy.viewport('iphone-4', 'landscape') - cy.wait(200) - - // The viewport will be reset back to the default dimensions - // in between tests (the default can be set in cypress.json) - }) -}) diff --git a/frontend/cypress/integration/examples/waiting.spec.js b/frontend/cypress/integration/examples/waiting.spec.js deleted file mode 100644 index c8f0d7c6723d27522415288acc22654c4752f694..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/waiting.spec.js +++ /dev/null @@ -1,31 +0,0 @@ -/// <reference types="cypress" /> - -context('Waiting', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/waiting') - }) - // BE CAREFUL of adding unnecessary wait times. - // https://on.cypress.io/best-practices#Unnecessary-Waiting - - // https://on.cypress.io/wait - it('cy.wait() - wait for a specific amount of time', () => { - cy.get('.wait-input1').type('Wait 1000ms after typing') - cy.wait(1000) - cy.get('.wait-input2').type('Wait 1000ms after typing') - cy.wait(1000) - cy.get('.wait-input3').type('Wait 1000ms after typing') - cy.wait(1000) - }) - - it('cy.wait() - wait for a specific route', () => { - // Listen to GET to comments/1 - cy.intercept('GET', '**/comments/*').as('getComment') - - // we have code that gets a comment when - // the button is clicked in scripts.js - cy.get('.network-btn').click() - - // wait for GET comments/1 - cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304]) - }) -}) diff --git a/frontend/cypress/integration/examples/window.spec.js b/frontend/cypress/integration/examples/window.spec.js deleted file mode 100644 index f94b64971db16f7f69b38958a77897b321ffeec8..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/examples/window.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -/// <reference types="cypress" /> - -context('Window', () => { - beforeEach(() => { - cy.visit('https://example.cypress.io/commands/window') - }) - - it('cy.window() - get the global window object', () => { - // https://on.cypress.io/window - cy.window().should('have.property', 'top') - }) - - it('cy.document() - get the document object', () => { - // https://on.cypress.io/document - cy.document().should('have.property', 'charset').and('eq', 'UTF-8') - }) - - it('cy.title() - get the title', () => { - // https://on.cypress.io/title - cy.title().should('include', 'Kitchen Sink') - }) -}) diff --git a/frontend/cypress/integration/exportWorkout_spec.js b/frontend/cypress/integration/exportWorkout_spec.js deleted file mode 100644 index 8854703889b9d57125a9bccfb23b07ef9c808a20..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/exportWorkout_spec.js +++ /dev/null @@ -1,53 +0,0 @@ -it('should export a planned workout', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("coach"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - - cy.get("#workout-1").click({force: true}); - - - cy.contains(" View/Edit Planned Workout"); - - const expectedHeaders = ["Subject", "Start date", "Start time", "Description"]; - - cy.get("#btn-export-workout").should("be.visible").click(); - - cy.readFile("cypress/downloads/event.csv").then(text => { - const content = text.split(",") - const headers = content.splice(0, 4); - let descriptionAndSubjectNameInOne = headers.pop(); - let descriptionAndSubjectArray = descriptionAndSubjectNameInOne.split("\n"); - let description = descriptionAndSubjectArray[0]; - description = description.substring(0, description.length - 1); - let subjectName = descriptionAndSubjectArray[1]; - headers.push(description); - content.unshift(subjectName); - - let descriptionContent = content.pop(); - descriptionContent = descriptionContent.substring(0, descriptionContent.length - 2); - content.push(descriptionContent); - - expect(headers).deep.to.equal(expectedHeaders); - - const csvDate = `${content[1]} ${content[2]}` - let date = new Date(csvDate); - - - - date = new Date(date.getTime() - date.getTimezoneOffset() * 60 * 1000).toISOString(); // get ISO format for local time - date = date.substring(0, date.length - 5); // remove Z (since this is a local time, not UTC) - - - cy.get('input[name="name"]').should("have.value", content[0]); - cy.get('input[name="date"]').should("have.value", date); - cy.get('textarea[name="notes"]').should("have.value", content[3]); - - - }) - - -}); diff --git a/frontend/cypress/integration/register_spec.js b/frontend/cypress/integration/register_spec.js deleted file mode 100644 index 4c16e53781af8d4aa48a4e68a03689a32919de9c..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/register_spec.js +++ /dev/null @@ -1,204 +0,0 @@ -describe("Boundary test - Phone number ", function () { - it("Finds register page", function () { - cy.visit("/register.html"); - cy.contains("Create Account"); - }); - - beforeEach(() => { - - cy.visit("register.html"); - cy.get('form'); - - cy.get('input[name="email"]').type("test@test.no").should("have.value", "test@test.no"); - cy.get('input[name="password"]').type("hei"); - cy.get('input[name="password1"]').type("hei"); - cy.get('input[name="country"]').type("nombia").should("have.value", "nombia"); - cy.get('input[name="city"]').type("lol").should("have.value", "lol"); - cy.get('input[name="street_address"]').type("lol").should("have.value", "lol"); - }); - - it("Checks boundary value for max+ phone number", function () { - cy.get('input[name="username"]').type("Testmax+phone").should("have.value", "Testmax+phone"); - cy.get('input[name="phone_number"]').type("9".repeat(51)); - cy.get("#btn-create-account").click(); - cy.contains("Registration failed!"); - cy.contains("Ensure this field has no more than 50 characters"); - }); - - it("Checks boundary value for min- phone number", function () { - cy.get('input[name="username"]').type("Testmin-phone").should("have.value", "Testmin-phone"); - - cy.get('input[name="phone_number"]'); - cy.get("#btn-create-account").click(); - - cy.contains("Registration failed!"); - }); - - it("Checks if a user can write letters in phone number", function () { - cy.get('input[name="username"]').type("Testletterphone").should("have.value", "Testletterphone"); - - - cy.get('input[name="phone_number"]').type("h".repeat(50)); - cy.get("#btn-create-account").click(); - - cy.contains("Registration failed!"); - cy.contains("Phone number can't contain letters"); - - }); - - it("Checks boundary value for max", function () { - - cy.get('input[name="username"]').type("Testmaxphone").should("have.value", "Testmaxphone"); - - - cy.get('input[name="phone_number"]').type("9".repeat(50)); - cy.get("#btn-create-account").click(); - - - cy.url().should('include', '/workouts.html'); - - cy.contains("Log out"); - cy.contains("SecFit"); - }); - - it("Checks boundary value for min", function () { - - cy.get('input[name="username"]').type("Testminphone").should("have.value", "Testminphone"); - - - cy.get('input[name="phone_number"]').type("9".repeat(1)); - cy.get("#btn-create-account").click(); - - - cy.url().should('include', '/workouts.html'); - - cy.contains("Log out"); - cy.contains("SecFit"); - }); - -}); - - -describe("Boundary test - user name ", function () { - it("Finds register page", function () { - cy.visit("/register.html"); - cy.contains("Create Account"); - }); - - beforeEach(() => { - - cy.visit("register.html"); - cy.get('form'); - - cy.get('input[name="email"]').type("test@test.no").should("have.value", "test@test.no"); - cy.get('input[name="password"]').type("hei"); - cy.get('input[name="password1"]').type("hei"); - cy.get('input[name="country"]').type("nombia").should("have.value", "nombia"); - cy.get('input[name="city"]').type("lol").should("have.value", "lol"); - cy.get('input[name="street_address"]').type("lol").should("have.value", "lol"); - ; - }); - - it("Checks boundary value for max+ username", function () { - cy.get('input[name="username"]').type("u".repeat(151)).should("have.value", "u".repeat(151)); - cy.get("#btn-create-account").click(); - cy.contains("Registration failed!"); - cy.contains("Ensure this field has no more than 150 characters"); - - }); - - it("Checks boundary value for min- username", function () { - cy.get("#btn-create-account").click(); - cy.contains("Registration failed!"); - cy.contains("This field may not be blank."); - }); - - - it("Checks boundary value for max username", function () { - - cy.get('input[name="username"]').type("u".repeat(150)).should("have.value", "u".repeat(150)); - cy.get("#btn-create-account").click(); - - cy.url().should('include', '/workouts.html'); - - cy.contains("Log out"); - cy.contains("SecFit"); - }); - - it("Checks boundary value for min username", function () { - - cy.get('input[name="username"]').type("u".repeat(1)).should("have.value", "u".repeat(1)); - cy.get("#btn-create-account").click(); - - cy.url().should('include', '/workouts.html'); - - cy.contains("Log out"); - cy.contains("SecFit"); - }); - -}); - - -describe("Boundary test - email ", function () { - it("Finds register page", function () { - cy.visit("/register.html"); - cy.contains("Create Account"); - }); - - beforeEach(() => { - - cy.visit("register.html"); - cy.get('form'); - - cy.get('input[name="password"]').type("hei"); - cy.get('input[name="password1"]').type("hei"); - cy.get('input[name="country"]').type("nombia").should("have.value", "nombia"); - cy.get('input[name="city"]').type("lol").should("have.value", "lol"); - cy.get('input[name="street_address"]').type("lol").should("have.value", "lol"); - ; - }); - - it("Checks boundary value for max+ email", function () { - cy.get('input[name="username"]').type("emailmax+").should("have.value", "emailmax+"); - cy.get('input[name="email"]').type("u".repeat(243) + "@test.no").should("have.value", "u".repeat(243)+ "@test.no"); - - cy.get("#btn-create-account").click(); - cy.contains("Registration failed!"); - cy.contains("Ensure this field has no more than 50 characters"); - - }); - - it("Checks boundary value for min- email", function () { - cy.get("#btn-create-account").click(); - cy.contains("Registration failed!"); - cy.contains("This field may not be blank."); - }); - - - it("Checks boundary value for max", function () { - - cy.get('input[name="username"]').type("emailmax").should("have.value", "emailmax"); - cy.get('input[name="email"]').type("u".repeat(242) + "@test.no").should("have.value", "u".repeat(242)+ "@test.no"); - - cy.get("#btn-create-account").click(); - - cy.url().should('include', '/workouts.html'); - - cy.contains("Log out"); - cy.contains("SecFit"); - }); - - it("Checks boundary value for min", function () { - - cy.get('input[name="username"]').type("emailmin").should("have.value", "emailmin"); - cy.get('input[name="email"]').type("n@n.no").should("have.value", "n@n.no"); - - cy.get("#btn-create-account").click(); - - cy.url().should('include', '/workouts.html'); - - cy.contains("Log out"); - cy.contains("SecFit"); - }); - -}); \ No newline at end of file diff --git a/frontend/cypress/integration/suggestedWorkout_spec.js b/frontend/cypress/integration/suggestedWorkout_spec.js deleted file mode 100644 index 72338ef8539573e9f88672b02b9ec30eea990bf0..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/suggestedWorkout_spec.js +++ /dev/null @@ -1,188 +0,0 @@ -describe('The Home Page', () => { - - it('successfully login', () => { - // this.currentUser will now point to the response - // body of the cy.request() that we could use - // to log in or work with in some way - - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("coach"); - cy.get('input[name="password"]').type("hei") - - cy.get('#rememberMe').check(); - - cy.get("#btn-login").click(); - - cy.url().should('include', '/workouts.html'); - }); - - it('should create a suggested workout', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("coach"); - cy.get('input[name="password"]').type("hei") - - cy.get('#rememberMe').check(); - - cy.get("#btn-login").click(); - - cy.url().should('include', '/workouts.html'); - - cy.get("#btn-suggest-workout").click(); - - cy.contains("Suggest Workout to Athlete") - - cy.get('input[name="name"]').type("Suggest workout test").should("have.value", "Suggest workout test"); - cy.get('textarea[name="notes"]').type("Suggest workout test notes").should("have.value", "Suggest workout test notes"); - cy.get('select[name="athlete"]').select("athlete"); - - cy.get('select[name="type"]').select("Plank", {force: true}); - cy.get('input[name="sets"]').type("3", {force: true}).should("have.value", "3"); - cy.get('input[name="number"]').type("3", {force: true}).should("have.value", "3"); - - cy.get("#btn-ok-workout").click(); - - cy.url().should('include', '/workouts.html'); - - cy.contains("Suggest workout test"); - cy.contains("test"); - - }); - - it('should cancel edit suggested workout', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("coach"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - cy.get("#suggested-workout-2").click({force:true}); - - cy.contains("Suggest Workout to Athlete") - - cy.get("#btn-edit-workout").click(); - - cy.get('textarea[name="notes"]').clear().type("Suggest workout test notes edit").should("have.value", "Suggest workout test notes edit"); - - cy.get("#btn-cancel-workout").click(); - - }); - - it('should edit suggested workout', function () { - - - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("coach"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - - cy.get("#suggested-workout-2").click({force:true}); - - cy.contains("Suggest Workout to Athlete") - - cy.get("#btn-edit-workout").click(); - - cy.get('textarea[name="notes"]').clear().type("Suggest workout test notes edit").should("have.value", "Suggest workout test notes edit"); - - cy.get("#btn-ok-workout").click(); - }); - - it('should be shown in "Suggested Workouts To Athletes"', function () { - - - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("coach"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - cy.contains("Suggested Workouts To Athletes").click(); - - cy.contains("Suggested Workout Seed"); - - - }); - - it('should be shown in "Suggested Workouts From Coach"', function () { - - - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("athlete"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - cy.contains("Suggested Workouts From Coach").click(); - - cy.contains("Suggested Workout Seed"); - }); - - it('should accept a suggested workout from coach', function () { - - - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("athlete"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - - cy.get("#suggested-workout-2").click({force:true}); - - cy.contains("Suggest Workout to Athlete"); - - cy.get('input[name="name"]').should("have.value", "Suggest workout test"); - cy.get('textarea[name="notes"]').should("have.value", "Suggest workout test notes edit"); - - cy.get('input[name="date"]').type("2021-01-06T13:31"); - - cy.get("#btn-accept-workout").click(); - - cy.contains("Could not create new workout!"); - - cy.contains("Date must be a future date"); - - - cy.get('input[name="date"]').type("2021-04-06T13:31"); - - - cy.get("#btn-accept-workout").click(); - - cy.url().should('include', '/workouts.html'); - - cy.contains("Owner: athlete"); - - }); - - it('should decline a suggested workout from coach', function () { - - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("athlete"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - - cy.get("#suggested-workout-1").click({force:true}); - - cy.contains("Suggested Workout from Coach"); - - cy.get("#btn-decline-workout").click(); - - cy.url().should('include', '/workouts.html'); - - cy.get("#suggested-workout-1").should('not.exist'); - - }); - - -}) \ No newline at end of file diff --git a/frontend/cypress/integration/workout_spec.js b/frontend/cypress/integration/workout_spec.js deleted file mode 100644 index ede5b482e3fcd5dc1aef12ca680f038aa7fb72d0..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/workout_spec.js +++ /dev/null @@ -1,168 +0,0 @@ - -describe("Workout", () => { - it('should create a planned workout', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("athlete"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - - cy.get("#btn-plan-workout").click(); - - - cy.contains(" View/Edit Planned Workout"); - - - cy.get('input[name="name"]').type("Plan workout test").should("have.value", "Plan workout test"); - cy.get('textarea[name="notes"]').type("Plan workout test notes").should("have.value", "Plan workout test notes"); - - - //cy.get('#btn-add-exercise').click(); - cy.get('select[name="type"]').select("Plank", {force: true}); - cy.get('input[name="sets"]').type("3", {force: true}).should("have.value", "3"); - cy.get('input[name="number"]').type("3", {force: true}).should("have.value", "3"); - - - - cy.get('input[name="date"]').type("2021-01-06T13:31"); - - cy.get("#btn-ok-workout").click(); - - - cy.contains("Could not create new workout!"); - - cy.contains("Date must be a future date"); - - cy.get('input[name="date"]').type("2021-04-06T13:31"); - - - cy.get("#btn-ok-workout").click(); - - cy.url().should('include', '/workouts.html'); - - cy.contains("Owner: athlete"); - cy.contains("Plan workout test") - }); - - - it('should create a logged workout', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("athlete"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - - cy.get("#btn-create-workout").click(); - - - cy.contains(" View/Edit Logged Workout"); - - - cy.get('input[name="name"]').type("Log workout test").should("have.value", "Log workout test"); - cy.get('textarea[name="notes"]').type("Log workout test notes").should("have.value", "Log workout test notes"); - - - //cy.get('#btn-add-exercise').click(); - cy.get('select[name="type"]').select("Plank", {force: true}); - cy.get('input[name="sets"]').type("3", {force: true}).should("have.value", "3"); - cy.get('input[name="number"]').type("3", {force: true}).should("have.value", "3"); - - - - cy.get('input[name="date"]').type("2021-06-06T13:31"); - - cy.get("#btn-ok-workout").click(); - - - cy.contains("Could not create new workout!"); - - cy.contains("Date must be an old date"); - - cy.get('input[name="date"]').type("2021-01-06T13:31"); - - - cy.get("#btn-ok-workout").click(); - - cy.url().should('include', '/workouts.html'); - - cy.contains("Owner: athlete"); - cy.contains("Log workout test") - }); - - it('should edit a planned workout', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("athlete"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - cy.contains("My Planned Workouts").click(); - - - cy.get("#workout-5").click(); - - - cy.contains(" View/Edit Planned Workout"); - - cy.get("#btn-edit-workout").click(); - - - cy.get('input[name="date"]').type("2021-01-06T13:31"); - - cy.get("#btn-ok-workout").click(); - - - cy.contains("Could not update workout!"); - - cy.contains("Date must be a future date"); - - cy.get('input[name="date"]').type("2021-05-06T13:31"); - - - cy.get("#btn-ok-workout").click(); - - - }); - - it('should edit a logged workout', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("athlete"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - cy.contains("My Logged Workouts").click(); - - - cy.get("#workout-4").click({force: true}); - - - cy.contains(" View/Edit Logged Workout"); - - cy.get("#btn-edit-workout").click(); - - - cy.get('input[name="date"]').type("2021-08-06T13:31"); - - cy.get("#btn-ok-workout").click(); - - - cy.contains("Could not update workout!"); - - cy.contains("Date must be an old date"); - - cy.get('input[name="date"]').type("2021-01-06T13:31"); - - - cy.get("#btn-ok-workout").click(); - - - }); - -}); \ No newline at end of file diff --git a/frontend/cypress/integration/workouts-visibilit_spec.js b/frontend/cypress/integration/workouts-visibilit_spec.js deleted file mode 100644 index bb89ff75b7ef481d1e380397ccf4c29e656c973b..0000000000000000000000000000000000000000 --- a/frontend/cypress/integration/workouts-visibilit_spec.js +++ /dev/null @@ -1,67 +0,0 @@ -describe("Visibilty workouts", () => { - - it('should test that public workout is visible for coach', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("coach"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - cy.contains("Public Workouts").click(); - - - cy.contains("Workout Public - Coach") - }); - - it('should test that public workout is visible for athlete', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("athlete"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - cy.contains("Public Workouts").click(); - - cy.contains("Workout Public - Coach") - cy.contains("Owner: coach") - - }); - - it('should test that only my workouts shows up in "My logged Workouts"', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("coach"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - cy.contains("My Logged Workouts").click(); - - cy.contains("My Planned Workouts").click(); - cy.contains("My Logged Workouts").click(); - - - cy.contains("Owner: athlete").should("not.be.visible"); - - }); - - it('should test that athlete workouts shows up in "Athlete Workouts"', function () { - cy.visit("/login.html"); - - cy.get('input[name="username"]').type("coach"); - cy.get('input[name="password"]').type("hei") - - cy.get("#btn-login").click(); - - cy.contains("Athlete Workouts").click(); - cy.contains("My Planned Workouts").click(); - cy.contains("Athlete Workouts").click(); - - - - cy.contains("Owner: athlete").should("be.visible"); - - }); -}) \ No newline at end of file diff --git a/frontend/cypress/plugins/index.js b/frontend/cypress/plugins/index.js deleted file mode 100644 index 91b5744284bb4c17082477ad09217408f0e81c9b..0000000000000000000000000000000000000000 --- a/frontend/cypress/plugins/index.js +++ /dev/null @@ -1,22 +0,0 @@ -/// <reference types="cypress" /> -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -/** - * @type {Cypress.PluginConfig} - */ -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config - -} diff --git a/frontend/cypress/support/commands.js b/frontend/cypress/support/commands.js deleted file mode 100644 index ca4d256f3eb15dfabad1f5760c9b2d0ceb4c24b9..0000000000000000000000000000000000000000 --- a/frontend/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/frontend/cypress/support/index.js b/frontend/cypress/support/index.js deleted file mode 100644 index d68db96df2697e0835f5c490db0c2cc81673f407..0000000000000000000000000000000000000000 --- a/frontend/cypress/support/index.js +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f7377b37e2094004497e334aefc279c7bbc6c116..684c2b9b1e095e4db27b92eb477e83a931359bf9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -4,108 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@cypress/listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "cli-cursor": "^1.0.2", - "date-fns": "^1.27.2", - "figures": "^1.7.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "@cypress/request": { - "version": "2.88.5", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz", - "integrity": "sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - } - } - }, - "@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, "@netflix/nerror": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@netflix/nerror/-/nerror-1.1.3.tgz", @@ -143,33 +41,6 @@ "fastq": "^1.6.0" } }, - "@samverschueren/stream-to-observable": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", - "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", - "dev": true, - "requires": { - "any-observable": "^0.3.0" - } - }, - "@types/node": { - "version": "12.12.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.50.tgz", - "integrity": "sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w==", - "dev": true - }, - "@types/sinonjs__fake-timers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", - "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==", - "dev": true - }, - "@types/sizzle": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", - "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", - "dev": true - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -186,18 +57,6 @@ "negotiator": "0.6.2" } }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "android-versions": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/android-versions/-/android-versions-1.5.0.tgz", @@ -213,18 +72,6 @@ "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=", "dev": true }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -234,69 +81,24 @@ "color-convert": "^1.9.0" } }, - "any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", - "dev": true - }, - "arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true - }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -309,33 +111,12 @@ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, "big-integer": { "version": "1.6.48", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", "dev": true }, - "blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -390,36 +171,12 @@ "fill-range": "^7.0.1" } }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, - "cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -431,76 +188,6 @@ "supports-color": "^5.3.0" } }, - "check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "^1.0.1" - } - }, - "cli-table3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", - "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", - "dev": true, - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^4.2.0" - } - }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "dev": true, - "requires": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -516,34 +203,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "optional": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true - }, - "common-tags": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", - "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", - "dev": true - }, "compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -574,18 +233,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -800,12 +447,6 @@ "which": "^1.3.0" } }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -819,178 +460,6 @@ "which": "^1.2.9" } }, - "csv-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz", - "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "cypress": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-6.6.0.tgz", - "integrity": "sha512-+Xx3Zn653LJHUsCb9h1Keql2jlazbr1ROmbY6DFJMmXKLgXP4ez9cE403W93JNGRbZK0Tng3R/oP8mvd9XAPVg==", - "dev": true, - "requires": { - "@cypress/listr-verbose-renderer": "^0.4.1", - "@cypress/request": "^2.88.5", - "@cypress/xvfb": "^1.2.4", - "@types/node": "12.12.50", - "@types/sinonjs__fake-timers": "^6.0.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.1.2", - "blob-util": "2.0.2", - "bluebird": "^3.7.2", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-table3": "~0.6.0", - "commander": "^5.1.0", - "common-tags": "^1.8.0", - "dayjs": "^1.9.3", - "debug": "4.3.2", - "eventemitter2": "^6.4.2", - "execa": "^4.0.2", - "executable": "^4.1.1", - "extract-zip": "^1.7.0", - "fs-extra": "^9.0.1", - "getos": "^3.2.1", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.2", - "lazy-ass": "^1.6.0", - "listr": "^0.14.3", - "lodash": "^4.17.19", - "log-symbols": "^4.0.0", - "minimist": "^1.2.5", - "moment": "^2.29.1", - "ospath": "^1.2.2", - "pretty-bytes": "^5.4.1", - "ramda": "~0.27.1", - "request-progress": "^3.0.0", - "supports-color": "^7.2.0", - "tmp": "~0.2.1", - "untildify": "^4.0.0", - "url": "^0.11.0", - "yauzl": "^2.10.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true - }, - "dayjs": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz", - "integrity": "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==", - "dev": true - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1006,12 +475,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1024,28 +487,12 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", - "dev": true - }, "elementtree": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", @@ -1055,12 +502,6 @@ "sax": "1.1.4" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -1105,12 +546,6 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, - "eventemitter2": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.4.tgz", - "integrity": "sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw==", - "dev": true - }, "execa": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", @@ -1171,29 +606,6 @@ } } }, - "executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "requires": { - "pify": "^2.2.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -1232,36 +644,12 @@ "vary": "~1.1.2" } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "dev": true, - "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - } - }, "extsprintf": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=", "dev": true }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, "fast-glob": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", @@ -1282,12 +670,6 @@ "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==", "dev": true }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, "fastq": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", @@ -1297,25 +679,6 @@ "reusify": "^1.0.4" } }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" - } - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1340,23 +703,6 @@ "unpipe": "~1.0.0" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -1395,24 +741,6 @@ "pump": "^3.0.0" } }, - "getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "dev": true, - "requires": { - "async": "^3.2.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -1436,46 +764,12 @@ "is-glob": "^4.0.1" } }, - "global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "dev": true, - "requires": { - "ini": "1.3.7" - } - }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1503,17 +797,6 @@ } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "human-signals": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", @@ -1529,12 +812,6 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1551,39 +828,18 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", - "dev": true - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -1593,103 +849,30 @@ "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dev": true, - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "dev": true, - "requires": { - "symbol-observable": "^1.1.0" - } - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", - "dev": true - }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true - }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -1699,291 +882,12 @@ "graceful-fs": "^4.1.6" } }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - } - } - }, - "lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", - "dev": true - }, - "listr": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", - "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", - "dev": true, - "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", - "is-observable": "^1.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.5.0", - "listr-verbose-renderer": "^0.5.0", - "p-map": "^2.0.0", - "rxjs": "^6.3.3" - }, - "dependencies": { - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - } - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", - "dev": true - }, - "listr-update-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", - "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "listr-verbose-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", - "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "cli-cursor": "^2.1.0", - "date-fns": "^1.27.2", - "figures": "^2.0.0" - }, - "dependencies": { - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - } - } - }, "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", - "dev": true - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "requires": { - "chalk": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "dependencies": { - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - } - } - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2060,52 +964,12 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", - "dev": true - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "neat-csv": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/neat-csv/-/neat-csv-6.0.1.tgz", - "integrity": "sha512-j87x1Rj2KiyZeOtFzedZXV8XN7Om/BcEcclPfj84I24yq9vfarNY8DrKKN8nfwN1FtQZgBvxTfQbwpugkV2MoA==", - "dev": true, - "requires": { - "csv-parser": "^3.0.0", - "get-stream": "^6.0.0", - "to-readable-stream": "^2.1.0" - }, - "dependencies": { - "get-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", - "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", - "dev": true - } - } - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -2145,24 +1009,6 @@ } } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "objectorarray": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.4.tgz", @@ -2233,24 +1079,12 @@ "os-tmpdir": "^1.0.0" } }, - "ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", - "dev": true - }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -2281,18 +1115,6 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -2316,18 +1138,6 @@ "xmldom": "0.1.x" } }, - "pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, "properties-parser": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/properties-parser/-/properties-parser-0.3.1.tgz", @@ -2347,12 +1157,6 @@ "ipaddr.js": "1.9.1" } }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -2363,12 +1167,6 @@ "once": "^1.3.1" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -2381,18 +1179,6 @@ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", "dev": true }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2429,78 +1215,18 @@ "with-open-file": "^0.1.6" } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "dev": true, - "requires": { - "throttleit": "^1.0.0" - } - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "^1.0.0", - "onetime": "^1.0.0" - }, - "dependencies": { - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - } - } - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, "run-parallel": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", "dev": true }, - "rxjs": { - "version": "6.6.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz", - "integrity": "sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2599,87 +1325,18 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, "string.prototype.codepointat": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==", "dev": true }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -2701,33 +1358,6 @@ "has-flag": "^3.0.0" } }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "to-readable-stream": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz", - "integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2743,37 +1373,6 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2784,12 +1383,6 @@ "mime-types": "~2.1.24" } }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, "underscore": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", @@ -2808,74 +1401,18 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "dev": true }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "dev": true }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -2896,49 +1433,6 @@ "pify": "^4.0.1" } }, - "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2956,16 +1450,6 @@ "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==", "dev": true - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } } } } diff --git a/frontend/package.json b/frontend/package.json index e530416543d6dddfaf8b99a1611bc74e1f46e169..1e59f6413916ecb87fcba68ece25bdf6f440c5e5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,10 +5,7 @@ "description": "A sample Apache Cordova application that responds to the deviceready event.", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "cypress:open": "cypress open", - "django:testserver": " cd ../backend/secfit && python manage.py testserver ../../frontend/cypress/fixtures/users.json ../../frontend/cypress/fixtures/suggestedWorkout.json ../../frontend/cypress/fixtures/workouts.json", - "test:dev": "cd www && python -m http.server 8001" + "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "ecosystem:cordova" @@ -20,9 +17,7 @@ "cordova-browser": "^6.0.0", "cordova-plugin-camera": "^4.1.0", "cordova-plugin-file": "^6.0.2", - "cordova-plugin-whitelist": "^1.3.4", - "cypress": "^6.6.0", - "neat-csv": "^6.0.1" + "cordova-plugin-whitelist": "^1.3.4" }, "cordova": { "plugins": { @@ -37,4 +32,4 @@ "android" ] } -} +} \ No newline at end of file diff --git a/frontend/www/plannedWorkout.html b/frontend/www/plannedWorkout.html index aaef77e7a40fe949742022772fc987bfc15abbca..f66b88c6b0e77fb44ff64a7465336dc7042fafa6 100644 --- a/frontend/www/plannedWorkout.html +++ b/frontend/www/plannedWorkout.html @@ -22,7 +22,7 @@ </div> <div class="row"> <div class="col-lg"> - <p class="mt-4">A planned workout is a future workout that will be autologged</p> + <p class="mt-4">A planned workout is a future workout that will be autologged</h3> </div> </div> <form class="row g-3 mb-4" id="form-workout"> diff --git a/frontend/www/scripts/scripts.js b/frontend/www/scripts/scripts.js index 8c550009c43a70acc44ecd56c4434faa318c1c69..0a421de933dea21a0128fa7f5140a99e016c9d3a 100644 --- a/frontend/www/scripts/scripts.js +++ b/frontend/www/scripts/scripts.js @@ -117,6 +117,7 @@ function setReadOnly(readOnly, selector) { selector = `input[type="file"], select[name="${key}`; for (let input of form.querySelectorAll(selector)) { + console.log(input); if ((!readOnly && input.hasAttribute("disabled"))) { input.disabled = false; diff --git a/frontend/www/scripts/suggestedworkout.js b/frontend/www/scripts/suggestedworkout.js index 080ba9d84894a1a6a21ddfa599b5bb42c2a00bf3..7d490eef3c2e02003643101f43a70fea2ea28885 100644 --- a/frontend/www/scripts/suggestedworkout.js +++ b/frontend/www/scripts/suggestedworkout.js @@ -52,7 +52,9 @@ async function retrieveWorkout(id, currentUser) { let input = form.querySelector("select:disabled"); // files let filesDiv = document.querySelector("#uploaded-files"); + console.log(workoutData.suggested_workout_files); for (let file of workoutData.suggested_workout_files) { + console.log("Her skal jeg") let a = document.createElement("a"); a.href = file.file; let pathArray = file.file.split("/"); @@ -197,9 +199,9 @@ function generateWorkoutForm() { let date = new Date(formData.get('date')).toISOString(); submitForm.append("date", date); submitForm.append("notes", formData.get("notes")); - //submitForm.append("owner", formData.get("coach_username")); + submitForm.append("owner", formData.get("coach_username")); submitForm.delete("athlete"); - submitForm.append("visibility", "CO"); + submitForm.append("visibility", "PU"); // adding exercise instances let exerciseInstances = []; @@ -238,6 +240,7 @@ function generateSuggestWorkoutForm() { submitForm.append("status", "p"); + console.log(formData.get("athlete")); // adding exercise instances let exerciseInstances = []; @@ -285,6 +288,7 @@ function handleCancelDuringWorkoutCreate() { } async function selectAthletesForSuggest(currentUser) { + console.log(currentUser) let suggestTypes = []; let suggestTemplate = document.querySelector("#template-suggest"); @@ -308,6 +312,7 @@ async function selectAthletesForSuggest(currentUser) { } let currentSuggestType = suggestTypes[0]; + console.log(currentSuggestType); suggestTypeSelect.value = currentSuggestType.id; let divSuggestWorkout = document.querySelector("#div-suggest-workout"); @@ -359,6 +364,7 @@ window.addEventListener("DOMContentLoaded", async () => { coachTitle = document.querySelector("#coach-title"); athleteTitle = document.querySelector("#athlete-title"); let postCommentButton = document.querySelector("#post-comment"); + let divCommentRow = document.querySelector("#div-comment-row"); let buttonAddExercise = document.querySelector("#btn-add-exercise"); let buttonRemoveExercise = document.querySelector("#btn-remove-exercise"); @@ -383,6 +389,7 @@ window.addEventListener("DOMContentLoaded", async () => { deleteWorkoutButton.addEventListener("click", (async (id) => await deleteSuggestedWorkout(id)).bind(undefined, id)); okWorkoutButton.addEventListener("click", (async (id) => await updateWorkout(id)).bind(undefined, id)); postCommentButton.addEventListener("click", (async (id) => await createComment(id)).bind(undefined, id)); + divCommentRow.className = divCommentRow.className.replace(" hide", ""); } @@ -399,6 +406,7 @@ window.addEventListener("DOMContentLoaded", async () => { declineWorkoutButton.addEventListener("click", (async (id) => await deleteSuggestedWorkout(id)).bind(undefined, id)); acceptWorkoutButton.addEventListener("click", (async (id) => await acceptWorkout(id)).bind(undefined, id)); postCommentButton.addEventListener("click", (async (id) => await createComment(id)).bind(undefined, id)); + divCommentRow.className = divCommentRow.className.replace(" hide", ""); } } else { await createBlankExercise(); @@ -429,6 +437,7 @@ window.addEventListener("DOMContentLoaded", async () => { okWorkoutButton.addEventListener("click", (async (currentUser) => await createSuggestWorkout(currentUser)).bind(undefined, currentUser)); cancelWorkoutButton.addEventListener("click", handleCancelDuringWorkoutCreate); + divCommentRow.className += " hide"; } }); \ No newline at end of file diff --git a/frontend/www/scripts/workout.js b/frontend/www/scripts/workout.js index 0658a485a795db99920880ef2d0ef64e136e23a6..8123d50937a9ec5a92d90bdec8426681a0409502 100644 --- a/frontend/www/scripts/workout.js +++ b/frontend/www/scripts/workout.js @@ -5,544 +5,518 @@ let editWorkoutButton; let exportWorkoutButton; let postCommentButton; let planned = false; -let plannedTitle; -let loggedTitle; -let plannedDescription; -let loggedDescription; async function retrieveWorkout(id) { - let workoutData = null; - let response = await sendRequest("GET", `${HOST}/api/workouts/${id}/`); - if (!response.ok) { - let data = await response.json(); - let alert = createAlert("Could not retrieve workout data!", data); - document.body.prepend(alert); - } else { - workoutData = await response.json(); - - - if (workoutData.planned) { - plannedTitle = document.querySelector("#title-planned"); - console.log(plannedTitle) - plannedDescription = document.querySelector("#description-planned"); - - plannedTitle.className = plannedTitle.className.replace("hide", ""); - plannedDescription.className = plannedDescription.className.replace("hide", ""); - } else { - loggedDescription = document.querySelector("#description-logged"); - loggedTitle = document.querySelector("#title-logged"); - - loggedTitle.className = loggedTitle.className.replace("hide", ""); - loggedDescription.className = loggedDescription.className.replace("hide", ""); + let workoutData = null; + let response = await sendRequest("GET", `${HOST}/api/workouts/${id}/`); + if (!response.ok) { + let data = await response.json(); + let alert = createAlert("Could not retrieve workout data!", data); + document.body.prepend(alert); + } else { + workoutData = await response.json(); + let form = document.querySelector("#form-workout"); + let formData = new FormData(form); + planned = workoutData.planned; + for (let key of formData.keys()) { + let selector = `input[name="${key}"], textarea[name="${key}"]`; + let input = form.querySelector(selector); + let newVal = workoutData[key]; + if (key == "date") { + // Creating a valid datetime-local string with the correct local time + let date = new Date(newVal); + date = new Date( + date.getTime() - date.getTimezoneOffset() * 60 * 1000 + ).toISOString(); // get ISO format for local time + newVal = date.substring(0, newVal.length - 1); // remove Z (since this is a local time, not UTC) + } + if (key != "files") { + input.value = newVal; + } + } - } + let input = form.querySelector("select:disabled"); + input.value = workoutData["visibility"]; + // files + let filesDiv = document.querySelector("#uploaded-files"); + for (let file of workoutData.files) { + let a = document.createElement("a"); + a.href = file.file; + let pathArray = file.file.split("/"); + a.text = pathArray[pathArray.length - 1]; + a.className = "me-2"; + filesDiv.appendChild(a); + } - let form = document.querySelector("#form-workout"); - let formData = new FormData(form); - planned = workoutData.planned; - for (let key of formData.keys()) { - let selector = `input[name="${key}"], textarea[name="${key}"]`; - let input = form.querySelector(selector); - let newVal = workoutData[key]; - if (key == "date") { - // Creating a valid datetime-local string with the correct local time - let date = new Date(newVal); - date = new Date( - date.getTime() - date.getTimezoneOffset() * 60 * 1000 - ).toISOString(); // get ISO format for local time - newVal = date.substring(0, newVal.length - 1); // remove Z (since this is a local time, not UTC) - } - if (key != "files") { - input.value = newVal; - } - } + // create exercises - let input = form.querySelector("select:disabled"); - input.value = workoutData["visibility"]; - // files - let filesDiv = document.querySelector("#uploaded-files"); - for (let file of workoutData.files) { - let a = document.createElement("a"); - a.href = file.file; - let pathArray = file.file.split("/"); - a.text = pathArray[pathArray.length - 1]; - a.className = "me-2"; - filesDiv.appendChild(a); - } + // fetch exercise types + let exerciseTypeResponse = await sendRequest( + "GET", + `${HOST}/api/exercises/` + ); + let exerciseTypes = await exerciseTypeResponse.json(); + + //TODO: This should be in its own method. + for (let i = 0; i < workoutData.exercise_instances.length; i++) { + let templateExercise = document.querySelector("#template-exercise"); + let divExerciseContainer = templateExercise.content.firstElementChild.cloneNode( + true + ); + + let exerciseTypeLabel = divExerciseContainer.querySelector( + ".exercise-type" + ); + exerciseTypeLabel.for = `inputExerciseType${i}`; + + let exerciseTypeSelect = divExerciseContainer.querySelector("select"); + exerciseTypeSelect.id = `inputExerciseType${i}`; + exerciseTypeSelect.disabled = true; - // create exercises - - // fetch exercise types - let exerciseTypeResponse = await sendRequest( - "GET", - `${HOST}/api/exercises/` - ); - let exerciseTypes = await exerciseTypeResponse.json(); - - //TODO: This should be in its own method. - for (let i = 0; i < workoutData.exercise_instances.length; i++) { - let templateExercise = document.querySelector("#template-exercise"); - let divExerciseContainer = templateExercise.content.firstElementChild.cloneNode( - true - ); - - let exerciseTypeLabel = divExerciseContainer.querySelector( - ".exercise-type" - ); - exerciseTypeLabel.for = `inputExerciseType${i}`; - - let exerciseTypeSelect = divExerciseContainer.querySelector("select"); - exerciseTypeSelect.id = `inputExerciseType${i}`; - exerciseTypeSelect.disabled = true; - - let splitUrl = workoutData.exercise_instances[i].exercise.split("/"); - let currentExerciseTypeId = splitUrl[splitUrl.length - 2]; - let currentExerciseType = ""; - - for (let j = 0; j < exerciseTypes.count; j++) { - let option = document.createElement("option"); - option.value = exerciseTypes.results[j].id; - if (currentExerciseTypeId == exerciseTypes.results[j].id) { - currentExerciseType = exerciseTypes.results[j]; - } - option.innerText = exerciseTypes.results[j].name; - exerciseTypeSelect.append(option); - } - - exerciseTypeSelect.value = currentExerciseType.id; - - let exerciseSetLabel = divExerciseContainer.querySelector( - ".exercise-sets" - ); - exerciseSetLabel.for = `inputSets${i}`; - - let exerciseSetInput = divExerciseContainer.querySelector( - "input[name='sets']" - ); - exerciseSetInput.id = `inputSets${i}`; - exerciseSetInput.value = workoutData.exercise_instances[i].sets; - exerciseSetInput.readOnly = true; - - let exerciseNumberLabel = divExerciseContainer.querySelector( - ".exercise-number" - ); - (exerciseNumberLabel.for = "for"), `inputNumber${i}`; - exerciseNumberLabel.innerText = currentExerciseType.unit; - - let exerciseNumberInput = divExerciseContainer.querySelector( - "input[name='number']" - ); - exerciseNumberInput.id = `inputNumber${i}`; - exerciseNumberInput.value = workoutData.exercise_instances[i].number; - exerciseNumberInput.readOnly = true; - - let exercisesDiv = document.querySelector("#div-exercises"); - exercisesDiv.appendChild(divExerciseContainer); + let splitUrl = workoutData.exercise_instances[i].exercise.split("/"); + let currentExerciseTypeId = splitUrl[splitUrl.length - 2]; + let currentExerciseType = ""; + + for (let j = 0; j < exerciseTypes.count; j++) { + let option = document.createElement("option"); + option.value = exerciseTypes.results[j].id; + if (currentExerciseTypeId == exerciseTypes.results[j].id) { + currentExerciseType = exerciseTypes.results[j]; } + option.innerText = exerciseTypes.results[j].name; + exerciseTypeSelect.append(option); + } + + exerciseTypeSelect.value = currentExerciseType.id; + + let exerciseSetLabel = divExerciseContainer.querySelector( + ".exercise-sets" + ); + exerciseSetLabel.for = `inputSets${i}`; + + let exerciseSetInput = divExerciseContainer.querySelector( + "input[name='sets']" + ); + exerciseSetInput.id = `inputSets${i}`; + exerciseSetInput.value = workoutData.exercise_instances[i].sets; + exerciseSetInput.readOnly = true; + + let exerciseNumberLabel = divExerciseContainer.querySelector( + ".exercise-number" + ); + (exerciseNumberLabel.for = "for"), `inputNumber${i}`; + exerciseNumberLabel.innerText = currentExerciseType.unit; + + let exerciseNumberInput = divExerciseContainer.querySelector( + "input[name='number']" + ); + exerciseNumberInput.id = `inputNumber${i}`; + exerciseNumberInput.value = workoutData.exercise_instances[i].number; + exerciseNumberInput.readOnly = true; + + let exercisesDiv = document.querySelector("#div-exercises"); + exercisesDiv.appendChild(divExerciseContainer); } - return workoutData; + } + return workoutData; } function handleCancelDuringWorkoutEdit() { - location.reload(); + location.reload(); } function handleEditWorkoutButtonClick() { - let addExerciseButton = document.querySelector("#btn-add-exercise"); - let removeExerciseButton = document.querySelector("#btn-remove-exercise"); - - setReadOnly(false, "#form-workout"); - document.querySelector("#inputOwner").readOnly = true; // owner field should still be readonly - - editWorkoutButton.className += " hide"; - exportWorkoutButton.className += " hide"; - okWorkoutButton.className = okWorkoutButton.className.replace(" hide", ""); - cancelWorkoutButton.className = cancelWorkoutButton.className.replace( - " hide", - "" - ); - deleteWorkoutButton.className = deleteWorkoutButton.className.replace( - " hide", - "" - ); - addExerciseButton.className = addExerciseButton.className.replace( - " hide", - "" - ); - removeExerciseButton.className = removeExerciseButton.className.replace( - " hide", - "" - ); - - cancelWorkoutButton.addEventListener("click", handleCancelDuringWorkoutEdit); + let addExerciseButton = document.querySelector("#btn-add-exercise"); + let removeExerciseButton = document.querySelector("#btn-remove-exercise"); + + setReadOnly(false, "#form-workout"); + document.querySelector("#inputOwner").readOnly = true; // owner field should still be readonly + + editWorkoutButton.className += " hide"; + exportWorkoutButton.className += " hide"; + okWorkoutButton.className = okWorkoutButton.className.replace(" hide", ""); + cancelWorkoutButton.className = cancelWorkoutButton.className.replace( + " hide", + "" + ); + deleteWorkoutButton.className = deleteWorkoutButton.className.replace( + " hide", + "" + ); + addExerciseButton.className = addExerciseButton.className.replace( + " hide", + "" + ); + removeExerciseButton.className = removeExerciseButton.className.replace( + " hide", + "" + ); + + cancelWorkoutButton.addEventListener("click", handleCancelDuringWorkoutEdit); } //Taken from github: https://gist.github.com/dannypule/48418b4cd8223104c6c92e3016fc0f61 function handleExportToCalendarClick(workoutData) { - const headers = { - subject: "Subject", - startDate: "Start date", - startTime: "Start time", - description: "Description", - }; - - const dataFormatted = []; - - const startTime = new Date(workoutData.date).toLocaleTimeString("en-us"); - const startDate = new Date(workoutData.date) - .toLocaleString("en-us", { - year: "numeric", - month: "2-digit", - day: "2-digit", - }) - .replace(/(\d+)\/(\d+)\/(\d+)/, "$1/$2/$3"); - - dataFormatted.push({ - subject: workoutData.name, - startDate: startDate, - startTime: startTime, - description: workoutData.notes, - }); - - - exportCSVFile(headers, dataFormatted, "event"); + const headers = { + subject: "Subject", + startDate: "Start date", + startTime: "Start time", + description: "Description", + }; + + const dataFormatted = []; + + const startTime = new Date(workoutData.date).toLocaleTimeString("en-us"); + const startDate = new Date(workoutData.date) + .toLocaleString("en-us", { + year: "numeric", + month: "2-digit", + day: "2-digit", + }) + .replace(/(\d+)\/(\d+)\/(\d+)/, "$1/$2/$3"); + + dataFormatted.push({ + subject: workoutData.name, + startDate: startDate, + startTime: startTime, + description: workoutData.notes, + }); + + console.log(dataFormatted); + + exportCSVFile(headers, dataFormatted, "event"); } //Taken from github: https://gist.github.com/dannypule/48418b4cd8223104c6c92e3016fc0f61 function convertToCSV(objArray) { - var array = typeof objArray != "object" ? JSON.parse(objArray) : objArray; - var str = ""; + var array = typeof objArray != "object" ? JSON.parse(objArray) : objArray; + var str = ""; - for (var i = 0; i < array.length; i++) { - var line = ""; - for (var index in array[i]) { - if (line != "") line += ","; + for (var i = 0; i < array.length; i++) { + var line = ""; + for (var index in array[i]) { + if (line != "") line += ","; - line += array[i][index]; - } - - str += line + "\r\n"; + line += array[i][index]; } - return str; + str += line + "\r\n"; + } + + return str; } //Taken from github: https://gist.github.com/dannypule/48418b4cd8223104c6c92e3016fc0f61 function exportCSVFile(headers, items, fileTitle) { - if (headers) { - items.unshift(headers); - } - - // Convert Object to JSON - var jsonObject = JSON.stringify(items); - - var csv = this.convertToCSV(jsonObject); - - var exportedFilenmae = fileTitle + ".csv" || "export.csv"; - - var blob = new Blob([csv], {type: "text/csv;charset=utf-8;"}); - if (navigator.msSaveBlob) { - // IE 10+ - navigator.msSaveBlob(blob, exportedFilenmae); - } else { - var link = document.createElement("a"); - if (link.download !== undefined) { - // feature detection - // Browsers that support HTML5 download attribute - var url = URL.createObjectURL(blob); - link.setAttribute("href", url); - link.setAttribute("download", exportedFilenmae); - link.style.visibility = "hidden"; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - } + console.log(items, headers); + if (headers) { + items.unshift(headers); + } + + // Convert Object to JSON + var jsonObject = JSON.stringify(items); + + var csv = this.convertToCSV(jsonObject); + + var exportedFilenmae = fileTitle + ".csv" || "export.csv"; + + var blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); + if (navigator.msSaveBlob) { + // IE 10+ + navigator.msSaveBlob(blob, exportedFilenmae); + } else { + var link = document.createElement("a"); + if (link.download !== undefined) { + // feature detection + // Browsers that support HTML5 download attribute + var url = URL.createObjectURL(blob); + link.setAttribute("href", url); + link.setAttribute("download", exportedFilenmae); + link.style.visibility = "hidden"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); } + } } async function deleteWorkout(id) { - let response = await sendRequest("DELETE", `${HOST}/api/workouts/${id}/`); - if (!response.ok) { - let data = await response.json(); - let alert = createAlert(`Could not delete workout ${id}!`, data); - document.body.prepend(alert); - } else { - window.location.replace("workouts.html"); - } + let response = await sendRequest("DELETE", `${HOST}/api/workouts/${id}/`); + if (!response.ok) { + let data = await response.json(); + let alert = createAlert(`Could not delete workout ${id}!`, data); + document.body.prepend(alert); + } else { + window.location.replace("workouts.html"); + } } async function updateWorkout(id) { - let submitForm = generateWorkoutForm(); - let response = await sendRequest( - "PUT", - `${HOST}/api/workouts/${id}/`, - submitForm, - "" - ); - if (!response.ok) { - let data = await response.json(); - let alert = createAlert("Could not update workout!", data); - document.body.prepend(alert); - } else { - location.reload(); - } + let submitForm = generateWorkoutForm(); + let response = await sendRequest( + "PUT", + `${HOST}/api/workouts/${id}/`, + submitForm, + "" + ); + if (!response.ok) { + let data = await response.json(); + let alert = createAlert("Could not update workout!", data); + document.body.prepend(alert); + } else { + location.reload(); + } } function generateWorkoutForm() { - var today = new Date().toISOString(); + var today = new Date().toISOString(); - document.querySelector("#inputDateTime").min = today; + document.querySelector("#inputDateTime").min = today; - let form = document.querySelector("#form-workout"); + let form = document.querySelector("#form-workout"); - let formData = new FormData(form); - let submitForm = new FormData(); - - submitForm.append("name", formData.get("name")); - let date = new Date(formData.get("date")).toISOString(); - submitForm.append("date", date); - submitForm.append("notes", formData.get("notes")); - submitForm.append("visibility", formData.get("visibility")); - - submitForm.append("planned", planned); - - // adding exercise instances - let exerciseInstances = []; - let exerciseInstancesTypes = formData.getAll("type"); - let exerciseInstancesSets = formData.getAll("sets"); - let exerciseInstancesNumbers = formData.getAll("number"); - for (let i = 0; i < exerciseInstancesTypes.length; i++) { - exerciseInstances.push({ - exercise: `${HOST}/api/exercises/${exerciseInstancesTypes[i]}/`, - number: exerciseInstancesNumbers[i], - sets: exerciseInstancesSets[i], - }); - }; - - submitForm.append("exercise_instances", JSON.stringify(exerciseInstances)); - // adding files - for (let file of formData.getAll("files")) { - submitForm.append("files", file); - } - return submitForm; + let formData = new FormData(form); + let submitForm = new FormData(); + + submitForm.append("name", formData.get("name")); + let date = new Date(formData.get("date")).toISOString(); + submitForm.append("date", date); + submitForm.append("notes", formData.get("notes")); + submitForm.append("visibility", formData.get("visibility")); + + submitForm.append("planned", planned); + + // adding exercise instances + let exerciseInstances = []; + let exerciseInstancesTypes = formData.getAll("type"); + let exerciseInstancesSets = formData.getAll("sets"); + let exerciseInstancesNumbers = formData.getAll("number"); + for (let i = 0; i < exerciseInstancesTypes.length; i++) { + exerciseInstances.push({ + exercise: `${HOST}/api/exercises/${exerciseInstancesTypes[i]}/`, + number: exerciseInstancesNumbers[i], + sets: exerciseInstancesSets[i], + }); + } + + submitForm.append("exercise_instances", JSON.stringify(exerciseInstances)); + // adding files + for (let file of formData.getAll("files")) { + submitForm.append("files", file); + } + return submitForm; } async function createWorkout() { - let submitForm = generateWorkoutForm(); + let submitForm = generateWorkoutForm(); - let response = await sendRequest( - "POST", - `${HOST}/api/workouts/`, - submitForm, - "" - ); + let response = await sendRequest( + "POST", + `${HOST}/api/workouts/`, + submitForm, + "" + ); - if (response.ok) { - window.location.replace("workouts.html"); - } else { - let data = await response.json(); - let alert = createAlert("Could not create new workout!", data); - document.body.prepend(alert); - } + if (response.ok) { + window.location.replace("workouts.html"); + } else { + let data = await response.json(); + let alert = createAlert("Could not create new workout!", data); + document.body.prepend(alert); + } } function handleCancelDuringWorkoutCreate() { - window.location.replace("workouts.html"); + window.location.replace("workouts.html"); } async function createBlankExercise() { - let form = document.querySelector("#form-workout"); + let form = document.querySelector("#form-workout"); - let exerciseTypeResponse = await sendRequest("GET", `${HOST}/api/exercises/`); - let exerciseTypes = await exerciseTypeResponse.json(); + let exerciseTypeResponse = await sendRequest("GET", `${HOST}/api/exercises/`); + let exerciseTypes = await exerciseTypeResponse.json(); - let exerciseTemplate = document.querySelector("#template-exercise"); - let divExerciseContainer = exerciseTemplate.content.firstElementChild.cloneNode( - true - ); - let exerciseTypeSelect = divExerciseContainer.querySelector("select"); + let exerciseTemplate = document.querySelector("#template-exercise"); + let divExerciseContainer = exerciseTemplate.content.firstElementChild.cloneNode( + true + ); + let exerciseTypeSelect = divExerciseContainer.querySelector("select"); - for (let i = 0; i < exerciseTypes.count; i++) { - let option = document.createElement("option"); - option.value = exerciseTypes.results[i].id; - option.innerText = exerciseTypes.results[i].name; - exerciseTypeSelect.append(option); - } + for (let i = 0; i < exerciseTypes.count; i++) { + let option = document.createElement("option"); + option.value = exerciseTypes.results[i].id; + option.innerText = exerciseTypes.results[i].name; + exerciseTypeSelect.append(option); + } - let currentExerciseType = exerciseTypes.results[0]; - exerciseTypeSelect.value = currentExerciseType.name; + let currentExerciseType = exerciseTypes.results[0]; + exerciseTypeSelect.value = currentExerciseType.name; - let divExercises = document.querySelector("#div-exercises"); - divExercises.appendChild(divExerciseContainer); + let divExercises = document.querySelector("#div-exercises"); + divExercises.appendChild(divExerciseContainer); } function removeExercise(event) { - let divExerciseContainers = document.querySelectorAll( - ".div-exercise-container" - ); - if (divExerciseContainers && divExerciseContainers.length > 0) { - divExerciseContainers[divExerciseContainers.length - 1].remove(); - } + let divExerciseContainers = document.querySelectorAll( + ".div-exercise-container" + ); + if (divExerciseContainers && divExerciseContainers.length > 0) { + divExerciseContainers[divExerciseContainers.length - 1].remove(); + } } function addComment(author, text, date, append) { - /* Taken from https://www.bootdey.com/snippets/view/Simple-Comment-panel#css*/ - let commentList = document.querySelector("#comment-list"); - let listElement = document.createElement("li"); - listElement.className = "media"; - let commentBody = document.createElement("div"); - commentBody.className = "media-body"; - let dateSpan = document.createElement("span"); - dateSpan.className = "text-muted pull-right me-1"; - let smallText = document.createElement("small"); - smallText.className = "text-muted"; - - if (date != "Now") { - let localDate = new Date(date); - smallText.innerText = localDate.toLocaleString(); - } else { - smallText.innerText = date; - } - - dateSpan.appendChild(smallText); - commentBody.appendChild(dateSpan); - - let strong = document.createElement("strong"); - strong.className = "text-success"; - strong.innerText = author; - commentBody.appendChild(strong); - let p = document.createElement("p"); - p.innerHTML = text; - - commentBody.appendChild(strong); - commentBody.appendChild(p); - listElement.appendChild(commentBody); - - if (append) { - commentList.append(listElement); - } else { - commentList.prepend(listElement); - } + /* Taken from https://www.bootdey.com/snippets/view/Simple-Comment-panel#css*/ + let commentList = document.querySelector("#comment-list"); + let listElement = document.createElement("li"); + listElement.className = "media"; + let commentBody = document.createElement("div"); + commentBody.className = "media-body"; + let dateSpan = document.createElement("span"); + dateSpan.className = "text-muted pull-right me-1"; + let smallText = document.createElement("small"); + smallText.className = "text-muted"; + + if (date != "Now") { + let localDate = new Date(date); + smallText.innerText = localDate.toLocaleString(); + } else { + smallText.innerText = date; + } + + dateSpan.appendChild(smallText); + commentBody.appendChild(dateSpan); + + let strong = document.createElement("strong"); + strong.className = "text-success"; + strong.innerText = author; + commentBody.appendChild(strong); + let p = document.createElement("p"); + p.innerHTML = text; + + commentBody.appendChild(strong); + commentBody.appendChild(p); + listElement.appendChild(commentBody); + + if (append) { + commentList.append(listElement); + } else { + commentList.prepend(listElement); + } } async function createComment(workoutid) { - let commentArea = document.querySelector("#comment-area"); - let content = commentArea.value; - let body = { - workout: `${HOST}/api/workouts/${workoutid}/`, - content: content, - }; - - let response = await sendRequest("POST", `${HOST}/api/comments/`, body); - if (response.ok) { - addComment(sessionStorage.getItem("username"), content, "Now", false); - } else { - let data = await response.json(); - let alert = createAlert("Failed to create comment!", data); - document.body.prepend(alert); - } + let commentArea = document.querySelector("#comment-area"); + let content = commentArea.value; + let body = { + workout: `${HOST}/api/workouts/${workoutid}/`, + content: content, + }; + + let response = await sendRequest("POST", `${HOST}/api/comments/`, body); + if (response.ok) { + addComment(sessionStorage.getItem("username"), content, "Now", false); + } else { + let data = await response.json(); + let alert = createAlert("Failed to create comment!", data); + document.body.prepend(alert); + } } async function retrieveComments(workoutid) { - let response = await sendRequest("GET", `${HOST}/api/comments/`); - if (!response.ok) { - let data = await response.json(); - let alert = createAlert("Could not retrieve comments!", data); - document.body.prepend(alert); - } else { - let data = await response.json(); - let comments = data.results; - for (let comment of comments) { - let splitArray = comment.workout.split("/"); - if (splitArray[splitArray.length - 2] == workoutid) { - addComment(comment.owner, comment.content, comment.timestamp, true); - } - } + let response = await sendRequest("GET", `${HOST}/api/comments/`); + if (!response.ok) { + let data = await response.json(); + let alert = createAlert("Could not retrieve comments!", data); + document.body.prepend(alert); + } else { + let data = await response.json(); + let comments = data.results; + for (let comment of comments) { + let splitArray = comment.workout.split("/"); + if (splitArray[splitArray.length - 2] == workoutid) { + addComment(comment.owner, comment.content, comment.timestamp, true); + } } + } } window.addEventListener("DOMContentLoaded", async () => { - cancelWorkoutButton = document.querySelector("#btn-cancel-workout"); - okWorkoutButton = document.querySelector("#btn-ok-workout"); - deleteWorkoutButton = document.querySelector("#btn-delete-workout"); - editWorkoutButton = document.querySelector("#btn-edit-workout"); - exportWorkoutButton = document.querySelector("#btn-export-workout"); - let postCommentButton = document.querySelector("#post-comment"); - let divCommentRow = document.querySelector("#div-comment-row"); - let buttonAddExercise = document.querySelector("#btn-add-exercise"); - let buttonRemoveExercise = document.querySelector("#btn-remove-exercise"); - - buttonAddExercise.addEventListener("click", createBlankExercise); - buttonRemoveExercise.addEventListener("click", removeExercise); - - const urlParams = new URLSearchParams(window.location.search); - let currentUser = await getCurrentUser(); - - if (urlParams.has("id")) { - const id = urlParams.get("id"); - let workoutData = await retrieveWorkout(id); - await retrieveComments(id); - - if (workoutData["owner"] == currentUser.url) { - editWorkoutButton.classList.remove("hide"); - exportWorkoutButton.classList.remove("hide"); - editWorkoutButton.addEventListener("click", handleEditWorkoutButtonClick); - exportWorkoutButton.addEventListener( - "click", - ((workoutData) => handleExportToCalendarClick(workoutData)).bind( - undefined, - workoutData - ) - ); - deleteWorkoutButton.addEventListener( - "click", - (async (id) => await deleteWorkout(id)).bind(undefined, id) - ); - okWorkoutButton.addEventListener( - "click", - (async (id) => await updateWorkout(id)).bind(undefined, id) - ); - postCommentButton.addEventListener( - "click", - (async (id) => await createComment(id)).bind(undefined, id) - ); - divCommentRow.className = divCommentRow.className.replace(" hide", ""); - } - } else { - await createBlankExercise(); - loggedTitle = document.querySelector("#title-logged"); - loggedDescription = document.querySelector("#description-logged"); - - loggedTitle.className = loggedTitle.className.replace("hide", ""); - loggedDescription.className = loggedDescription.className.replace("hide", ""); - - let ownerInput = document.querySelector("#inputOwner"); - ownerInput.value = currentUser.username; - setReadOnly(false, "#form-workout"); - ownerInput.readOnly = !ownerInput.readOnly; - - okWorkoutButton.className = okWorkoutButton.className.replace(" hide", ""); - cancelWorkoutButton.className = cancelWorkoutButton.className.replace( - " hide", - "" - ); - buttonAddExercise.className = buttonAddExercise.className.replace( - " hide", - "" - ); - buttonRemoveExercise.className = buttonRemoveExercise.className.replace( - " hide", - "" - ); - - okWorkoutButton.addEventListener( - "click", - async () => await createWorkout() - ); - cancelWorkoutButton.addEventListener( - "click", - handleCancelDuringWorkoutCreate - ); - divCommentRow.className += " hide"; + cancelWorkoutButton = document.querySelector("#btn-cancel-workout"); + okWorkoutButton = document.querySelector("#btn-ok-workout"); + deleteWorkoutButton = document.querySelector("#btn-delete-workout"); + editWorkoutButton = document.querySelector("#btn-edit-workout"); + exportWorkoutButton = document.querySelector("#btn-export-workout"); + let postCommentButton = document.querySelector("#post-comment"); + let divCommentRow = document.querySelector("#div-comment-row"); + let buttonAddExercise = document.querySelector("#btn-add-exercise"); + let buttonRemoveExercise = document.querySelector("#btn-remove-exercise"); + + buttonAddExercise.addEventListener("click", createBlankExercise); + buttonRemoveExercise.addEventListener("click", removeExercise); + + const urlParams = new URLSearchParams(window.location.search); + let currentUser = await getCurrentUser(); + + if (urlParams.has("id")) { + const id = urlParams.get("id"); + let workoutData = await retrieveWorkout(id); + await retrieveComments(id); + + if (workoutData["owner"] == currentUser.url) { + editWorkoutButton.classList.remove("hide"); + exportWorkoutButton.classList.remove("hide"); + editWorkoutButton.addEventListener("click", handleEditWorkoutButtonClick); + exportWorkoutButton.addEventListener( + "click", + ((workoutData) => handleExportToCalendarClick(workoutData)).bind( + undefined, + workoutData + ) + ); + deleteWorkoutButton.addEventListener( + "click", + (async (id) => await deleteWorkout(id)).bind(undefined, id) + ); + okWorkoutButton.addEventListener( + "click", + (async (id) => await updateWorkout(id)).bind(undefined, id) + ); + postCommentButton.addEventListener( + "click", + (async (id) => await createComment(id)).bind(undefined, id) + ); + divCommentRow.className = divCommentRow.className.replace(" hide", ""); } + } else { + await createBlankExercise(); + let ownerInput = document.querySelector("#inputOwner"); + ownerInput.value = currentUser.username; + setReadOnly(false, "#form-workout"); + ownerInput.readOnly = !ownerInput.readOnly; + + okWorkoutButton.className = okWorkoutButton.className.replace(" hide", ""); + cancelWorkoutButton.className = cancelWorkoutButton.className.replace( + " hide", + "" + ); + buttonAddExercise.className = buttonAddExercise.className.replace( + " hide", + "" + ); + buttonRemoveExercise.className = buttonRemoveExercise.className.replace( + " hide", + "" + ); + + okWorkoutButton.addEventListener( + "click", + async () => await createWorkout() + ); + cancelWorkoutButton.addEventListener( + "click", + handleCancelDuringWorkoutCreate + ); + divCommentRow.className += " hide"; + } }); diff --git a/frontend/www/scripts/workouts.js b/frontend/www/scripts/workouts.js index dfce30c1d4143b7f416adc39f6109b318a2bccee..f01af08fa0a57055abc0434fd27be6e9cc49c74a 100644 --- a/frontend/www/scripts/workouts.js +++ b/frontend/www/scripts/workouts.js @@ -1,45 +1,44 @@ async function fetchWorkouts(ordering) { - let response = await sendRequest( - "GET", - `${HOST}/api/workouts/?ordering=${ordering}` - ); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } else { - let data = await response.json(); - - let workouts = data.results; - let container = document.getElementById("div-content"); - workouts.forEach((workout) => { - let templateWorkout = document.querySelector("#template-workout"); - let cloneWorkout = templateWorkout.content.cloneNode(true); - - let aWorkout = cloneWorkout.querySelector("a"); - aWorkout.href = `workout.html?id=${workout.id}`; - aWorkout.id = `workout-${workout.id}` - - let h5 = aWorkout.querySelector("h5"); - h5.textContent = workout.name; - - let localDate = new Date(workout.date); - - let table = aWorkout.querySelector("table"); - let rows = table.querySelectorAll("tr"); - rows[0].querySelectorAll( - "td" - )[1].textContent = localDate.toLocaleDateString(); // Date - rows[1].querySelectorAll( - "td" - )[1].textContent = localDate.toLocaleTimeString(); // Time - rows[2].querySelectorAll("td")[1].textContent = workout.owner_username; //Owner - rows[3].querySelectorAll("td")[1].textContent = - workout.exercise_instances.length; // Exercises - - container.appendChild(aWorkout); - }); - return workouts; - } + let response = await sendRequest( + "GET", + `${HOST}/api/workouts/?ordering=${ordering}` + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } else { + let data = await response.json(); + + let workouts = data.results; + let container = document.getElementById("div-content"); + workouts.forEach((workout) => { + let templateWorkout = document.querySelector("#template-workout"); + let cloneWorkout = templateWorkout.content.cloneNode(true); + + let aWorkout = cloneWorkout.querySelector("a"); + aWorkout.href = `workout.html?id=${workout.id}`; + + let h5 = aWorkout.querySelector("h5"); + h5.textContent = workout.name; + + let localDate = new Date(workout.date); + + let table = aWorkout.querySelector("table"); + let rows = table.querySelectorAll("tr"); + rows[0].querySelectorAll( + "td" + )[1].textContent = localDate.toLocaleDateString(); // Date + rows[1].querySelectorAll( + "td" + )[1].textContent = localDate.toLocaleTimeString(); // Time + rows[2].querySelectorAll("td")[1].textContent = workout.owner_username; //Owner + rows[3].querySelectorAll("td")[1].textContent = + workout.exercise_instances.length; // Exercises + + container.appendChild(aWorkout); + }); + return workouts; + } } async function fetchSuggestedWorkouts() { @@ -61,7 +60,6 @@ async function fetchSuggestedWorkouts() { let aWorkout = cloneWorkout.querySelector("a"); aWorkout.href = `suggestworkout.html?id=${workout.id}`; - aWorkout.id = `suggested-workout-${workout.id}`; let h5 = aWorkout.querySelector("h5"); h5.textContent = workout.name; @@ -85,7 +83,7 @@ async function fetchSuggestedWorkouts() { function createWorkout() { - window.location.replace("workout.html"); + window.location.replace("workout.html"); } function suggestWorkout() { @@ -93,41 +91,41 @@ function suggestWorkout() { } function planWorkout() { - window.location.replace("plannedWorkout.html"); + window.location.replace("plannedWorkout.html"); } window.addEventListener("DOMContentLoaded", async () => { - let createButton = document.querySelector("#btn-create-workout"); - let suggestButton = document.querySelector("#btn-suggest-workout"); + let createButton = document.querySelector("#btn-create-workout"); + let suggestButton = document.querySelector("#btn-suggest-workout"); suggestButton.addEventListener("click", suggestWorkout); createButton.addEventListener("click", createWorkout); - let planButton = document.querySelector("#btn-plan-workout"); - planButton.addEventListener("click", planWorkout); - let ordering = "-date"; - - const urlParams = new URLSearchParams(window.location.search); - if (urlParams.has("ordering")) { - let aSort = null; - ordering = urlParams.get("ordering"); + let planButton = document.querySelector("#btn-plan-workout"); + planButton.addEventListener("click", planWorkout); + let ordering = "-date"; + + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has("ordering")) { + let aSort = null; + ordering = urlParams.get("ordering"); if (ordering == "name" || ordering == "owner" || ordering == "date") { let aSort = document.querySelector(`a[href="?ordering=${ordering}"`); aSort.href = `?ordering=-${ordering}`; } } - let currentSort = document.querySelector("#current-sort"); - currentSort.innerHTML = - (ordering.startsWith("-") ? "Descending" : "Ascending") + - " " + - ordering.replace("-", ""); + let currentSort = document.querySelector("#current-sort"); + currentSort.innerHTML = + (ordering.startsWith("-") ? "Descending" : "Ascending") + + " " + + ordering.replace("-", ""); - let currentUser = await getCurrentUser(); - // grab username - if (ordering.includes("owner")) { - ordering += "__username"; - } - let workouts = await fetchWorkouts(ordering); - let [athleteWorkout, coachWorkout] = await fetchSuggestedWorkouts(); + let currentUser = await getCurrentUser(); + // grab username + if (ordering.includes("owner")) { + ordering += "__username"; + } + let workouts = await fetchWorkouts(ordering); +let [athleteWorkout, coachWorkout] = await fetchSuggestedWorkouts(); let allWorkouts = workouts.concat(athleteWorkout, coachWorkout); @@ -135,52 +133,59 @@ window.addEventListener("DOMContentLoaded", async () => { for (let i = 0; i < tabEls.length; i++) { let tabEl = tabEls[i]; tabEl.addEventListener("show.bs.tab", function (event) { - let workoutAnchors = document.querySelectorAll(".workout"); - for (let j = 0; j < allWorkouts.length; j++) { - // I'm assuming that the order of workout objects matches - // the other of the workout anchor elements. They should, given - // that I just created them. - let workout = allWorkouts[j]; - let workoutAnchor = workoutAnchors[j]; - - console.log(workout) - - switch (event.currentTarget.id) { - case "list-my-logged-workouts-list": - if (workout.owner == currentUser.url && !workout.planned) { - console.log(workout.owner) - workoutAnchor.classList.remove("hide"); - } else { - workoutAnchor.classList.add("hide"); - } + let workoutAnchors = document.querySelectorAll(".workout"); + for (let j = 0; j < allWorkouts.length; j++) { + // I'm assuming that the order of workout objects matches + // the other of the workout anchor elements. They should, given + // that I just created them. + let workout = allWorkouts[j]; + let workoutAnchor = workoutAnchors[j]; + + switch (event.currentTarget.id) { + case "list-my-logged-workouts-list": + if (workout.owner == currentUser.url) { + workoutAnchor.classList.remove("hide"); + } else { + workoutAnchor.classList.add("hide"); + } - break; - case "list-my-planned-workouts-list": - if (workout.owner == currentUser.ur && workout.planned) { - workoutAnchor.classList.remove("hide"); - } else { - workoutAnchor.classList.add("hide"); - } + if (!workout.planned) { + workoutAnchor.classList.remove("hide"); + } else { + workoutAnchor.classList.add("hide"); + } + break; + case "list-my-planned-workouts-list": + if (workout.owner == currentUser.url) { + workoutAnchor.classList.remove("hide"); + } else { + workoutAnchor.classList.add("hide"); + } - break; - case "list-athlete-workouts-list": - if ( - currentUser.athletes && - currentUser.athletes.includes(workout.owner) - ) { - workoutAnchor.classList.remove("hide"); - } else { - workoutAnchor.classList.add("hide"); - } - break; - case "list-public-workouts-list": - if (workout.visibility == "PU") { - workoutAnchor.classList.remove("hide"); - } else { - workoutAnchor.classList.add("hide"); - } - break; - case "list-suggested-coach-workouts-list": + if (workout.planned) { + workoutAnchor.classList.remove("hide"); + } else { + workoutAnchor.classList.add("hide"); + } + break; + case "list-athlete-workouts-list": + if ( + currentUser.athletes && + currentUser.athletes.includes(workout.owner) + ) { + workoutAnchor.classList.remove("hide"); + } else { + workoutAnchor.classList.add("hide"); + } + break; + case "list-public-workouts-list": + if (workout.visibility == "PU") { + workoutAnchor.classList.remove("hide"); + } else { + workoutAnchor.classList.add("hide"); + } + break; + case "list-suggested-coach-workouts-list": if (currentUser.coach) { let coachID = currentUser?.coach?.split('/'); if (coachID[coachID.length - 2] == workout.coach) { @@ -207,9 +212,9 @@ window.addEventListener("DOMContentLoaded", async () => { default : workoutAnchor.classList.remove("hide"); - break; - } - } - }); - } + break; + } + } + }); + } }); diff --git a/frontend/www/workout.html b/frontend/www/workout.html index 0f845db362d41d68c5c3b7abf26acb58672023c2..849b3fa093ad8ac03b08fe95801728260af00a84 100644 --- a/frontend/www/workout.html +++ b/frontend/www/workout.html @@ -17,15 +17,12 @@ <div class="container"> <div class="row"> <div class="col-lg"> - <h3 class="mt-3 hide" id="title-logged">View/Edit Logged Workout</h3> - <h3 class="mt-3 hide" id="title-planned">View/Edit Planned Workout</h3> - + <h3 class="mt-3">View/Edit Logged Workout</h3> </div> </div> <div class="row"> <div class="col-lg"> - <p class="mt-4 hide" id="description-planned" >A planned workout is a future workout that will be autologged</p> - <p class="mt-4 hide" id="description-logged" >A logged workout is a workout you have completed</p> + <p class="mt-4">A logged workout is a workout you have completed</p> </div> </div> <form class="row g-3 mb-4" id="form-workout"> diff --git a/frontend/www/workouts.html b/frontend/www/workouts.html index afc31b417ba942d184182201eaf869d52f04fd7a..195c27a60658fed654aa8588f132c9a42952f72c 100644 --- a/frontend/www/workouts.html +++ b/frontend/www/workouts.html @@ -43,10 +43,10 @@ <a class="list-group-item list-group-item-action active" id="list-all-workouts-list" data-bs-toggle="list" href="#list-all-workouts" role="tab" aria-controls="all">All Workouts</a> <a class="list-group-item list-group-item-action" id="list-my-logged-workouts-list" - data-bs-toggle="list" href="#list-my-workouts" role="tab" aria-controls="my">My Logged Workouts</a> + data-bs-toggle="list" href="#list-my-workouts" role="tab" aria-controls="my">My logged Workouts</a> <a class="list-group-item list-group-item-action" id="list-my-planned-workouts-list" data-bs-toggle="list" href="#list-my-planned-workouts" role="tab" aria-controls="my">My Planned - Workouts</a> + workouts</a> <a class="list-group-item list-group-item-action" id="list-athlete-workouts-list" data-bs-toggle="list" href="#list-athlete-workouts" role="tab" aria-controls="athlete">Athlete Workouts</a> <a class="list-group-item list-group-item-action" id="list-public-workouts-list" data-bs-toggle="list" @@ -81,68 +81,68 @@ </div> <template id="template-workout"> - <a - class="list-group-item list-group-item-action flex-column align-items-start my-1 workout" - > + <a + class="list-group-item list-group-item-action flex-column align-items-start my-1 workout" + > <div class="d-flex w-100 justify-content-between align-items-center"> - <h5 class="mb-1"></h5> + <h5 class="mb-1"></h5> </div> <div class="d-flex"> - <table class="mb-1 text-start"> - <tr> - <td>Date:</td> - <td></td> - </tr> - <tr> - <td>Time:</td> - <td></td> - </tr> - <tr> - <td>Owner:</td> - <td></td> - </tr> - <tr> - <td>Exercises:</td> - <td></td> - </tr> - </table> + <table class="mb-1 text-start"> + <tr> + <td>Date:</td> + <td></td> + </tr> + <tr> + <td>Time:</td> + <td></td> + </tr> + <tr> + <td>Owner:</td> + <td></td> + </tr> + <tr> + <td>Exercises:</td> + <td></td> + </tr> + </table> </div> - </a> + </a> </template> -<template id="template-suggested-workout"> - <a - class="list-group-item list-group-item-action flex-column align-items-start my-1 workout" - > + <template id="template-suggested-workout"> + <a + class="list-group-item list-group-item-action flex-column align-items-start my-1 workout" + > <div class="d-flex w-100 justify-content-between align-items-center"> - <h5 class="mb-1"></h5> + <h5 class="mb-1"></h5> </div> <div class="d-flex"> - <table class="mb-1 text-start"> - <tr> - <td>Owner:</td> - <td></td> - </tr> - <tr> - <td>Exercises:</td> - <td></td> - </tr> - <tr> - <td>Status:</td> - <td></td> - </tr> - </table> + <table class="mb-1 text-start"> + <tr> + <td>Owner:</td> + <td></td> + </tr> + <tr> + <td>Exercises:</td> + <td></td> + </tr> + <tr> + <td>Status:</td> + <td></td> + </tr> + </table> </div> - </a> -</template> + </a> + </template> <script src="scripts/defaults.js"></script> <script src="scripts/scripts.js"></script> <script src="scripts/workouts.js"></script> -<script - src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" - integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" - crossorigin="anonymous" -></script> -</body> + <script + src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" + integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" + crossorigin="anonymous" + ></script> + </body> </html>