From 14b64bb26b083ab133391b98e93382219337e738 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Fri, 2 Aug 2024 04:13:23 +0000 Subject: [PATCH 01/39] config: #2 prettier on save --- .devcontainer/vue-container/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/vue-container/devcontainer.json b/.devcontainer/vue-container/devcontainer.json index 51ab5c7..fe6cfad 100644 --- a/.devcontainer/vue-container/devcontainer.json +++ b/.devcontainer/vue-container/devcontainer.json @@ -22,7 +22,8 @@ "formatOnPaste": true, "quickSuggestions": { "strings": "on" - } + }, + "defaultFormatter": "esbenp.prettier-vscode" }, "tailwindCSS": { "includeLanguages": { From 33b1f17fc65700cd5d1c2b9e2ca44b9bd3b9fa97 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Fri, 2 Aug 2024 04:14:42 +0000 Subject: [PATCH 02/39] feat: #2 CompanyService scaffold --- .../company_services_controller.rb | 53 ++++++++ backend/app/models/company_service.rb | 13 ++ .../_company_service.json.jbuilder | 2 + .../company_services/index.json.jbuilder | 1 + .../views/company_services/show.json.jbuilder | 1 + backend/config/routes.rb | 1 + .../20240802031906_create_company_services.rb | 11 ++ backend/db/schema.rb | 25 ++++ backend/spec/factories/company_services.rb | 18 +++ backend/spec/models/company_service_spec.rb | 16 +++ .../spec/requests/company_services_spec.rb | 127 ++++++++++++++++++ .../routing/company_services_routing_spec.rb | 30 +++++ 12 files changed, 298 insertions(+) create mode 100644 backend/app/controllers/company_services_controller.rb create mode 100644 backend/app/models/company_service.rb create mode 100644 backend/app/views/company_services/_company_service.json.jbuilder create mode 100644 backend/app/views/company_services/index.json.jbuilder create mode 100644 backend/app/views/company_services/show.json.jbuilder create mode 100644 backend/db/migrate/20240802031906_create_company_services.rb create mode 100644 backend/db/schema.rb create mode 100644 backend/spec/factories/company_services.rb create mode 100644 backend/spec/models/company_service_spec.rb create mode 100644 backend/spec/requests/company_services_spec.rb create mode 100644 backend/spec/routing/company_services_routing_spec.rb diff --git a/backend/app/controllers/company_services_controller.rb b/backend/app/controllers/company_services_controller.rb new file mode 100644 index 0000000..64e3a37 --- /dev/null +++ b/backend/app/controllers/company_services_controller.rb @@ -0,0 +1,53 @@ +class CompanyServicesController < ApplicationController + before_action :set_company_service, only: %i[ show update destroy ] + + # GET /company_services + # GET /company_services.json + def index + @company_services = CompanyService.all + end + + # GET /company_services/1 + # GET /company_services/1.json + def show + end + + # POST /company_services + # POST /company_services.json + def create + @company_service = CompanyService.new(company_service_params) + + if @company_service.save + render :show, status: :created, location: @company_service + else + render json: @company_service.errors, status: :unprocessable_entity + end + end + + # PATCH/PUT /company_services/1 + # PATCH/PUT /company_services/1.json + def update + if @company_service.update(company_service_params) + render :show, status: :ok, location: @company_service + else + render json: @company_service.errors, status: :unprocessable_entity + end + end + + # DELETE /company_services/1 + # DELETE /company_services/1.json + def destroy + @company_service.destroy! + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_company_service + @company_service = CompanyService.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def company_service_params + params.require(:company_service).permit(:name, :contract_start_date, :contract_end_date) + end +end diff --git a/backend/app/models/company_service.rb b/backend/app/models/company_service.rb new file mode 100644 index 0000000..3d16c72 --- /dev/null +++ b/backend/app/models/company_service.rb @@ -0,0 +1,13 @@ +# == Schema Information +# +# Table name: company_services +# +# id :bigint not null, primary key +# name :string +# contract_start_date :datetime +# contract_end_date :datetime +# created_at :datetime not null +# updated_at :datetime not null +# +class CompanyService < ApplicationRecord +end diff --git a/backend/app/views/company_services/_company_service.json.jbuilder b/backend/app/views/company_services/_company_service.json.jbuilder new file mode 100644 index 0000000..2bf158a --- /dev/null +++ b/backend/app/views/company_services/_company_service.json.jbuilder @@ -0,0 +1,2 @@ +json.extract! company_service, :id, :name, :contract_start_date, :contract_end_date, :created_at, :updated_at +json.url company_service_url(company_service, format: :json) diff --git a/backend/app/views/company_services/index.json.jbuilder b/backend/app/views/company_services/index.json.jbuilder new file mode 100644 index 0000000..6cff28b --- /dev/null +++ b/backend/app/views/company_services/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @company_services, partial: "company_services/company_service", as: :company_service diff --git a/backend/app/views/company_services/show.json.jbuilder b/backend/app/views/company_services/show.json.jbuilder new file mode 100644 index 0000000..f6a52f0 --- /dev/null +++ b/backend/app/views/company_services/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "company_services/company_service", company_service: @company_service diff --git a/backend/config/routes.rb b/backend/config/routes.rb index a125ef0..0d0947a 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -1,4 +1,5 @@ Rails.application.routes.draw do + resources :company_services # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. diff --git a/backend/db/migrate/20240802031906_create_company_services.rb b/backend/db/migrate/20240802031906_create_company_services.rb new file mode 100644 index 0000000..6890219 --- /dev/null +++ b/backend/db/migrate/20240802031906_create_company_services.rb @@ -0,0 +1,11 @@ +class CreateCompanyServices < ActiveRecord::Migration[7.1] + def change + create_table :company_services do |t| + t.string :name + t.datetime :contract_start_date + t.datetime :contract_end_date + + t.timestamps + end + end +end diff --git a/backend/db/schema.rb b/backend/db/schema.rb new file mode 100644 index 0000000..8ea75aa --- /dev/null +++ b/backend/db/schema.rb @@ -0,0 +1,25 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.1].define(version: 2024_08_02_031906) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "company_services", force: :cascade do |t| + t.string "name" + t.datetime "contract_start_date" + t.datetime "contract_end_date" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end diff --git a/backend/spec/factories/company_services.rb b/backend/spec/factories/company_services.rb new file mode 100644 index 0000000..235c287 --- /dev/null +++ b/backend/spec/factories/company_services.rb @@ -0,0 +1,18 @@ +# == Schema Information +# +# Table name: company_services +# +# id :bigint not null, primary key +# name :string +# contract_start_date :datetime +# contract_end_date :datetime +# created_at :datetime not null +# updated_at :datetime not null +# +FactoryBot.define do + factory :company_service do + name { "MyString" } + contract_start_date { "2024-08-02 03:19:06" } + contract_end_date { "2024-08-02 03:19:06" } + end +end diff --git a/backend/spec/models/company_service_spec.rb b/backend/spec/models/company_service_spec.rb new file mode 100644 index 0000000..469fcca --- /dev/null +++ b/backend/spec/models/company_service_spec.rb @@ -0,0 +1,16 @@ +# == Schema Information +# +# Table name: company_services +# +# id :bigint not null, primary key +# name :string +# contract_start_date :datetime +# contract_end_date :datetime +# created_at :datetime not null +# updated_at :datetime not null +# +require 'rails_helper' + +RSpec.describe CompanyService, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/backend/spec/requests/company_services_spec.rb b/backend/spec/requests/company_services_spec.rb new file mode 100644 index 0000000..e443de0 --- /dev/null +++ b/backend/spec/requests/company_services_spec.rb @@ -0,0 +1,127 @@ +require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to test the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. + +RSpec.describe "/company_services", type: :request do + # This should return the minimal set of attributes required to create a valid + # CompanyService. As you add validations to CompanyService, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + + # This should return the minimal set of values that should be in the headers + # in order to pass any filters (e.g. authentication) defined in + # CompanyServicesController, or in your router and rack + # middleware. Be sure to keep this updated too. + let(:valid_headers) { + {} + } + + describe "GET /index" do + it "renders a successful response" do + CompanyService.create! valid_attributes + get company_services_url, headers: valid_headers, as: :json + expect(response).to be_successful + end + end + + describe "GET /show" do + it "renders a successful response" do + company_service = CompanyService.create! valid_attributes + get company_service_url(company_service), as: :json + expect(response).to be_successful + end + end + + describe "POST /create" do + context "with valid parameters" do + it "creates a new CompanyService" do + expect { + post company_services_url, + params: { company_service: valid_attributes }, headers: valid_headers, as: :json + }.to change(CompanyService, :count).by(1) + end + + it "renders a JSON response with the new company_service" do + post company_services_url, + params: { company_service: valid_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:created) + expect(response.content_type).to match(a_string_including("application/json")) + end + end + + context "with invalid parameters" do + it "does not create a new CompanyService" do + expect { + post company_services_url, + params: { company_service: invalid_attributes }, as: :json + }.to change(CompanyService, :count).by(0) + end + + it "renders a JSON response with errors for the new company_service" do + post company_services_url, + params: { company_service: invalid_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:unprocessable_entity) + expect(response.content_type).to match(a_string_including("application/json")) + end + end + end + + describe "PATCH /update" do + context "with valid parameters" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested company_service" do + company_service = CompanyService.create! valid_attributes + patch company_service_url(company_service), + params: { company_service: new_attributes }, headers: valid_headers, as: :json + company_service.reload + skip("Add assertions for updated state") + end + + it "renders a JSON response with the company_service" do + company_service = CompanyService.create! valid_attributes + patch company_service_url(company_service), + params: { company_service: new_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:ok) + expect(response.content_type).to match(a_string_including("application/json")) + end + end + + context "with invalid parameters" do + it "renders a JSON response with errors for the company_service" do + company_service = CompanyService.create! valid_attributes + patch company_service_url(company_service), + params: { company_service: invalid_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:unprocessable_entity) + expect(response.content_type).to match(a_string_including("application/json")) + end + end + end + + describe "DELETE /destroy" do + it "destroys the requested company_service" do + company_service = CompanyService.create! valid_attributes + expect { + delete company_service_url(company_service), headers: valid_headers, as: :json + }.to change(CompanyService, :count).by(-1) + end + end +end diff --git a/backend/spec/routing/company_services_routing_spec.rb b/backend/spec/routing/company_services_routing_spec.rb new file mode 100644 index 0000000..1dd58ff --- /dev/null +++ b/backend/spec/routing/company_services_routing_spec.rb @@ -0,0 +1,30 @@ +require "rails_helper" + +RSpec.describe CompanyServicesController, type: :routing do + describe "routing" do + it "routes to #index" do + expect(get: "/company_services").to route_to("company_services#index") + end + + it "routes to #show" do + expect(get: "/company_services/1").to route_to("company_services#show", id: "1") + end + + + it "routes to #create" do + expect(post: "/company_services").to route_to("company_services#create") + end + + it "routes to #update via PUT" do + expect(put: "/company_services/1").to route_to("company_services#update", id: "1") + end + + it "routes to #update via PATCH" do + expect(patch: "/company_services/1").to route_to("company_services#update", id: "1") + end + + it "routes to #destroy" do + expect(delete: "/company_services/1").to route_to("company_services#destroy", id: "1") + end + end +end From f87e8edfca95608988a19231e46d3f0514428612 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 16:50:47 +0000 Subject: [PATCH 03/39] feat: #2 add db seeds and define company services api response --- .../views/company_services/index.json.jbuilder | 7 ++++++- backend/config/environments/development.rb | 1 + backend/db/seeds.rb | 18 +++++++++--------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/backend/app/views/company_services/index.json.jbuilder b/backend/app/views/company_services/index.json.jbuilder index 6cff28b..81f17dc 100644 --- a/backend/app/views/company_services/index.json.jbuilder +++ b/backend/app/views/company_services/index.json.jbuilder @@ -1 +1,6 @@ -json.array! @company_services, partial: "company_services/company_service", as: :company_service +json.data do + json.array! @company_services, partial: "company_services/company_service", as: :company_service +end + +json.status 200 +json.statusText "OK" \ No newline at end of file diff --git a/backend/config/environments/development.rb b/backend/config/environments/development.rb index f962d9f..fad806a 100644 --- a/backend/config/environments/development.rb +++ b/backend/config/environments/development.rb @@ -1,6 +1,7 @@ require "active_support/core_ext/integer/time" Rails.application.configure do + config.hosts << "rails" # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded any time diff --git a/backend/db/seeds.rb b/backend/db/seeds.rb index 4fbd6ed..a32107f 100644 --- a/backend/db/seeds.rb +++ b/backend/db/seeds.rb @@ -1,9 +1,9 @@ -# This file should ensure the existence of records required to run the application in every environment (production, -# development, test). The code here should be idempotent so that it can be executed at any point in every environment. -# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). -# -# Example: -# -# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| -# MovieGenre.find_or_create_by!(name: genre_name) -# end +require 'faker' + +10.times do + company_service = CompanyService.create( + name: Faker::Company.unique.name, + contract_start_date: Faker::Date.backward(days: 30), + contract_end_date: Faker::Date.forward(days: 30) + ) +end \ No newline at end of file From 190df8ec0e7dcdccaf47c5865bae056584906e0c Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 16:53:06 +0000 Subject: [PATCH 04/39] config: #2 ignore ide files --- backend/.gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/backend/.gitignore b/backend/.gitignore index 7e6b54f..7ead876 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -31,3 +31,14 @@ # Ignore master key for decrypting credentials and more. /config/master.key + +# IDE and Editor directories +.idea/ +.vscode/ +*.sublime-workspace +*.sublime-project +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? \ No newline at end of file From d94a19219ea4278514cf242a042b9290b90b29d4 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 16:54:24 +0000 Subject: [PATCH 05/39] config: #2 vue connection with rails api --- frontend/vue.config.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 0aed899..bc18bd8 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -1,4 +1,18 @@ const { defineConfig } = require("@vue/cli-service"); +const API_URL = "http://rails:3000"; + module.exports = defineConfig({ transpileDependencies: true, + devServer: { + proxy: { + "/api": { + // target: process.env.RAILS_API_URL, + target: API_URL, + changeOrigin: true, + pathRewrite: { + "^/api": "", + }, + }, + }, + }, }); From 58eaa6a49e12ef77434ac80fbd79dbbc7ec00254 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 18:23:46 +0000 Subject: [PATCH 06/39] config: #2 smoke test playwright --- .../e2e/checkCompanyServiceShifts.spec.js | 10 ++ frontend/e2e/example.spec.js | 7 - frontend/package.json | 5 +- frontend/playwright.config.js | 4 +- frontend/src/App.vue | 21 +-- frontend/yarn.lock | 165 +++++++++++++++--- 6 files changed, 166 insertions(+), 46 deletions(-) create mode 100644 frontend/e2e/checkCompanyServiceShifts.spec.js delete mode 100644 frontend/e2e/example.spec.js diff --git a/frontend/e2e/checkCompanyServiceShifts.spec.js b/frontend/e2e/checkCompanyServiceShifts.spec.js new file mode 100644 index 0000000..f69afc0 --- /dev/null +++ b/frontend/e2e/checkCompanyServiceShifts.spec.js @@ -0,0 +1,10 @@ +const { test, expect } = require("@playwright/test"); + +test.describe("Check Company Service Shifts", () => { + test("Page has the correct title", async ({ page }) => { + await page.goto("/"); + await expect(page).toHaveTitle(/Company Service Shifts/); + const h1 = await page.locator("h1"); + await expect(h1).toHaveText("Company Service Shifts"); + }); +}); diff --git a/frontend/e2e/example.spec.js b/frontend/e2e/example.spec.js deleted file mode 100644 index f1798cf..0000000 --- a/frontend/e2e/example.spec.js +++ /dev/null @@ -1,7 +0,0 @@ -// const { test, expect } = require('@playwright/test'); -import { test, expect } from '@playwright/test'; - -test('homepage has expected title', async ({ page }) => { - await page.goto('http://vue:8080'); // Replace with your app's URL - await expect(page).toHaveTitle(/Your App Title/); // Replace with your expected title -}); diff --git a/frontend/package.json b/frontend/package.json index 11db6ca..a263659 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,9 @@ "serve": "vue-cli-service serve", "build": "vue-cli-service build", "test:unit": "vue-cli-service test:unit", - "lint": "vue-cli-service lint" + "lint": "vue-cli-service lint", + "test:e2e": "start-server-and-test serve http://localhost:8080 test:playwright", + "test:playwright": "playwright test" }, "dependencies": { "core-js": "^3.8.3", @@ -34,6 +36,7 @@ "jest": "^27.0.5", "postcss": "^8.4.40", "prettier": "^2.4.1", + "start-server-and-test": "^2.0.5", "tailwindcss": "^3.4.7", "ts-jest": "^27.0.4", "typescript": "~4.5.5" diff --git a/frontend/playwright.config.js b/frontend/playwright.config.js index 67fe036..f83f65f 100644 --- a/frontend/playwright.config.js +++ b/frontend/playwright.config.js @@ -1,9 +1,11 @@ -// const { defineConfig } = require('@playwright/test'); import { defineConfig } from "@playwright/test"; export default defineConfig({ testDir: "./e2e", outputDir: "./e2e/test-results/", + use: { + baseURL: "http://localhost:8080", + }, timeout: 30000, expect: { timeout: 5000, diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 8f4cf2a..aeadbcb 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,27 +1,18 @@ - + diff --git a/frontend/yarn.lock b/frontend/yarn.lock index e810a9d..940c1ab 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2668,6 +2668,15 @@ autoprefixer@^10.2.4, autoprefixer@^10.4.19: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +axios@^1.6.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.3.tgz#a1125f2faf702bc8e8f2104ec3a76fab40257d85" + integrity sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-jest@^27.0.6, babel-jest@^27.1.0, babel-jest@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" @@ -2804,7 +2813,7 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@^3.1.1: +bluebird@3.7.2, bluebird@^3.1.1: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -3008,6 +3017,11 @@ char-regex@^2.0.0: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.1.tgz#6dafdb25f9d3349914079f010ba8d0e6ff9cd01e" integrity sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw== +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== + chokidar@^3.4.2, chokidar@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" @@ -3527,7 +3541,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@4.3.6, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.6" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== @@ -3731,7 +3745,7 @@ dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -duplexer@^0.1.2: +duplexer@^0.1.2, duplexer@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== @@ -4089,6 +4103,19 @@ event-pubsub@4.3.0: resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e" integrity sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ== +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -4099,6 +4126,21 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +execa@5.1.1, execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + execa@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" @@ -4125,21 +4167,6 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -4326,7 +4353,7 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -follow-redirects@^1.0.0: +follow-redirects@^1.0.0, follow-redirects@^1.15.6: version "1.15.6" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== @@ -4367,6 +4394,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -4382,6 +4418,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== + fs-extra@^9.0.0, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -5540,7 +5581,7 @@ jiti@^1.21.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== -joi@^17.4.0: +joi@^17.11.0, joi@^17.4.0: version "17.13.3" resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec" integrity sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA== @@ -5723,6 +5764,11 @@ launch-editor@^2.2.1, launch-editor@^2.6.0, launch-editor@^2.8.0: picocolors "^1.0.0" shell-quote "^1.8.1" +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -5910,6 +5956,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== + mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" @@ -6033,7 +6084,7 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -6466,6 +6517,13 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== + dependencies: + through "~2.3" + picocolors@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" @@ -6909,6 +6967,18 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -7186,6 +7256,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -7519,6 +7596,13 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== + dependencies: + through "2" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -7548,6 +7632,20 @@ stackframe@^1.3.4: resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== +start-server-and-test@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-2.0.5.tgz#55397e581531ee54000c51c0078226ed7e35afa3" + integrity sha512-2CV4pz69NJVJKQmJeSr+O+SPtOreu0yxvhPmSXclzmAKkPREuMabyMh+Txpzemjx0RDzXOcG2XkhiUuxjztSQw== + dependencies: + arg "^5.0.2" + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.3.6" + execa "5.1.1" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "7.2.0" + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -7558,6 +7656,13 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== + dependencies: + duplexer "~0.1.1" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -7870,6 +7975,11 @@ throat@^6.0.1: resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== +through@2, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -7969,7 +8079,7 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.3: +tslib@^2.0.3, tslib@^2.1.0: version "2.6.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== @@ -8216,6 +8326,17 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +wait-on@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-7.2.0.tgz#d76b20ed3fc1e2bebc051fae5c1ff93be7892928" + integrity sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ== + dependencies: + axios "^1.6.1" + joi "^17.11.0" + lodash "^4.17.21" + minimist "^1.2.8" + rxjs "^7.8.1" + walker@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" From a0bdde11d30b43f351fc6e0126471159ffcfefcb Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 22:56:49 +0000 Subject: [PATCH 07/39] feat: #2 define e2e test to choose and filter services and their weeks --- .../e2e/checkCompanyServiceShifts.spec.js | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/frontend/e2e/checkCompanyServiceShifts.spec.js b/frontend/e2e/checkCompanyServiceShifts.spec.js index f69afc0..f79ed8e 100644 --- a/frontend/e2e/checkCompanyServiceShifts.spec.js +++ b/frontend/e2e/checkCompanyServiceShifts.spec.js @@ -4,7 +4,23 @@ test.describe("Check Company Service Shifts", () => { test("Page has the correct title", async ({ page }) => { await page.goto("/"); await expect(page).toHaveTitle(/Company Service Shifts/); - const h1 = await page.locator("h1"); - await expect(h1).toHaveText("Company Service Shifts"); + + const mainTitle = await page.locator("h1"); + await expect(mainTitle).toHaveText("Company Service Shifts"); + }); + + test("Shows dropdown with options", async ({ page }) => { + await page.goto("/"); + + // Select a service + await page.selectOption("select#companyService", "Service A"); + // Select a week + await page.selectOption("select#week", "Week 32 in 2024"); + + // Show Subtitle + const weekRangeDatesTitle = await page.locator("h2"); + await expect(weekRangeDatesTitle).toHaveText( + "From 04/08/2024 to 10/08/2024" + ); }); }); From 6f0f756ccc16682b5e883002315bf9d80ec9a971 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 23:10:57 +0000 Subject: [PATCH 08/39] feat: #2 define api mocks of company_services --- frontend/src/api/CompanyServiceApi.ts | 6 ++++++ frontend/src/api/types.ts | 10 ++++++++++ frontend/src/mock/company_services.json | 24 ++++++++++++++++++++++++ frontend/tsconfig.json | 21 +++++---------------- 4 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 frontend/src/api/CompanyServiceApi.ts create mode 100644 frontend/src/api/types.ts create mode 100644 frontend/src/mock/company_services.json diff --git a/frontend/src/api/CompanyServiceApi.ts b/frontend/src/api/CompanyServiceApi.ts new file mode 100644 index 0000000..c5240c4 --- /dev/null +++ b/frontend/src/api/CompanyServiceApi.ts @@ -0,0 +1,6 @@ +import { CompanyService } from "./types"; + +export async function fetchCompanyServices(): Promise { + const response = await import("@/mock/company_services.json"); + return response.default.data; +} diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts new file mode 100644 index 0000000..edea817 --- /dev/null +++ b/frontend/src/api/types.ts @@ -0,0 +1,10 @@ +export interface ApiResponse { + data: CompanyService[]; +} + +export interface CompanyService { + id: number; + name: string; + contract_start_date: string; + contract_end_date: string; +} diff --git a/frontend/src/mock/company_services.json b/frontend/src/mock/company_services.json new file mode 100644 index 0000000..9ac307e --- /dev/null +++ b/frontend/src/mock/company_services.json @@ -0,0 +1,24 @@ +{ + "data": [ + { + "id": 1, + "name": "Service A", + "contract_start_date": "2024-08-01T00:00:00Z", + "contract_end_date": "2024-12-31T23:59:59Z" + }, + { + "id": 2, + "name": "Service B", + "contract_start_date": "2024-07-01T00:00:00Z", + "contract_end_date": "2024-11-30T23:59:59Z" + }, + { + "id": 3, + "name": "Service C", + "contract_start_date": "2024-09-01T00:00:00Z", + "contract_end_date": "2025-01-31T23:59:59Z" + } + ], + "status": 200, + "statusText": "OK" +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 524b4fb..b50ffaf 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -5,6 +5,7 @@ "strict": true, "jsx": "preserve", "moduleResolution": "node", + "resolveJsonModule": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, @@ -12,21 +13,11 @@ "useDefineForClassFields": true, "sourceMap": true, "baseUrl": ".", - "types": [ - "webpack-env", - "jest" - ], + "types": ["webpack-env", "jest"], "paths": { - "@/*": [ - "src/*" - ] + "@/*": ["src/*"] }, - "lib": [ - "esnext", - "dom", - "dom.iterable", - "scripthost" - ] + "lib": ["esnext", "dom", "dom.iterable", "scripthost"] }, "include": [ "src/**/*.ts", @@ -35,7 +26,5 @@ "tests/**/*.ts", "tests/**/*.tsx" ], - "exclude": [ - "node_modules" - ] + "exclude": ["node_modules"] } From 92bfa190c0d640dfbb50f6c52a378ffd8326710f Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 23:27:54 +0000 Subject: [PATCH 09/39] feat: #2 dropdown component with mocked company services --- frontend/jest.config.js | 1 + frontend/src/App.vue | 6 +- .../CompanyServiceDropdown.vue | 58 ++++++++ .../__tests__/CompanyServiceDropdown.page.ts | 24 ++++ .../__tests__/CompanyServiceDropdown.spec.ts | 38 +++++ frontend/src/components/HelloWorld.vue | 132 ------------------ .../components/__tests__/HelloWorld.spec.ts | 12 -- frontend/src/views/CompanyServiceView.vue | 36 +++++ 8 files changed, 162 insertions(+), 145 deletions(-) create mode 100644 frontend/src/components/CompanyServiceSchedule/CompanyServiceDropdown.vue create mode 100644 frontend/src/components/CompanyServiceSchedule/__tests__/CompanyServiceDropdown.page.ts create mode 100644 frontend/src/components/CompanyServiceSchedule/__tests__/CompanyServiceDropdown.spec.ts delete mode 100644 frontend/src/components/HelloWorld.vue delete mode 100644 frontend/src/components/__tests__/HelloWorld.spec.ts create mode 100644 frontend/src/views/CompanyServiceView.vue diff --git a/frontend/jest.config.js b/frontend/jest.config.js index f9d5bfe..d8cf967 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -1,3 +1,4 @@ module.exports = { preset: "@vue/cli-plugin-unit-jest/presets/typescript-and-babel", + testPathIgnorePatterns: [".*\\.page\\.ts$"], }; diff --git a/frontend/src/App.vue b/frontend/src/App.vue index aeadbcb..f243881 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,14 +1,18 @@ + + diff --git a/frontend/src/components/CompanyServiceSchedule/__tests__/CompanyServiceDropdown.page.ts b/frontend/src/components/CompanyServiceSchedule/__tests__/CompanyServiceDropdown.page.ts new file mode 100644 index 0000000..c3c2fc4 --- /dev/null +++ b/frontend/src/components/CompanyServiceSchedule/__tests__/CompanyServiceDropdown.page.ts @@ -0,0 +1,24 @@ +import { VueWrapper } from "@vue/test-utils"; +import { nextTick } from "vue"; + +export class CompanyServiceDropdownPage { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private wrapper: VueWrapper; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(wrapper: VueWrapper) { + this.wrapper = wrapper; + } + + async wait() { + // await this.wrapper.vm.$nextTick(); + await nextTick(); + } + + get serviceDropdown() { + return this.wrapper.find("select#companyService"); + } + + get serviceOptions() { + return this.serviceDropdown.findAll("option"); + } +} diff --git a/frontend/src/components/CompanyServiceSchedule/__tests__/CompanyServiceDropdown.spec.ts b/frontend/src/components/CompanyServiceSchedule/__tests__/CompanyServiceDropdown.spec.ts new file mode 100644 index 0000000..c21280f --- /dev/null +++ b/frontend/src/components/CompanyServiceSchedule/__tests__/CompanyServiceDropdown.spec.ts @@ -0,0 +1,38 @@ +import { mount } from "@vue/test-utils"; +import { CompanyServiceDropdownPage } from "./CompanyServiceDropdown.page"; +import CompanyServiceDropdown from "@/components/CompanyServiceSchedule/CompanyServiceDropdown.vue"; +import * as api from "@/api/CompanyServiceApi"; + +jest.mock("@/api/CompanyServiceApi"); // Mock the API module + +describe("CompanyServiceDropdown.vue", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let wrapper: any; + let page: CompanyServiceDropdownPage; + + const mockFetchCompanyServices = jest.fn(); + + beforeEach(async () => { + // Mocking the return values for the company services + mockFetchCompanyServices.mockResolvedValue([ + { id: 1, name: "Service A" }, + { id: 2, name: "Service B" }, + ]); + + // Mocking the API functions + (api.fetchCompanyServices as jest.Mock).mockImplementation( + mockFetchCompanyServices + ); + + wrapper = mount(CompanyServiceDropdown); + page = new CompanyServiceDropdownPage(wrapper); + page.wait(); + }); + + it("renders the service dropdown and options", () => { + expect(page.serviceDropdown.exists()).toBe(true); + expect(page.serviceOptions.length).toBe(3); // Two services + expect(page.serviceOptions[0].text()).toBe("-- Select Service --"); + expect(page.serviceOptions[1].text()).toBe("Service A"); + }); +}); diff --git a/frontend/src/components/HelloWorld.vue b/frontend/src/components/HelloWorld.vue deleted file mode 100644 index 7711494..0000000 --- a/frontend/src/components/HelloWorld.vue +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - diff --git a/frontend/src/components/__tests__/HelloWorld.spec.ts b/frontend/src/components/__tests__/HelloWorld.spec.ts deleted file mode 100644 index 0811d8a..0000000 --- a/frontend/src/components/__tests__/HelloWorld.spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { shallowMount } from "@vue/test-utils"; -import HelloWorld from "@/components/HelloWorld.vue"; - -describe("HelloWorld.vue", () => { - it("renders props.msg when passed", () => { - const msg = "new message"; - const wrapper = shallowMount(HelloWorld, { - props: { msg }, - }); - expect(wrapper.text()).toMatch(msg); - }); -}); diff --git a/frontend/src/views/CompanyServiceView.vue b/frontend/src/views/CompanyServiceView.vue new file mode 100644 index 0000000..fd29d6c --- /dev/null +++ b/frontend/src/views/CompanyServiceView.vue @@ -0,0 +1,36 @@ + + + + + From 3ccdf3e0c0e03afb9fd44904fec6fe52d80cb725 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 23:28:21 +0000 Subject: [PATCH 10/39] config: #2 define testing workflow --- .github/workflows/test.yml | 89 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..1e948a0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,89 @@ +name: test + +on: push + +jobs: + backend: + runs-on: ubuntu-latest + defaults: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3.4' + + - name: Install dependencies + run: | + cd backend + bundle install + + - name: Run Rails tests + run: | + cd backend + bin/rails test + + frontend: + runs-on: ubuntu-latest + needs: backend # Ensure frontend tests run after backend tests + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '21' + + - name: Install dependencies + run: | + cd frontend + yarn install + + - name: Run Jest tests + run: | + cd frontend + yarn test:unit + + - name: Start Rails server + run: | + cd backend + bin/rails server -e test & # Run the server in the background + + - name: Wait for Rails server to be ready + run: | + until curl -s http://localhost:3000 > /dev/null; do + echo "Waiting for Rails server..." + sleep 2 + done + + - name: Run Playwright tests + run: | + cd frontend + yarn test:e2e + + # frontend-e2e: + # runs-on: ubuntu-latest + # needs: backend # Ensure frontend tests run after backend tests + # steps: + # - name: Checkout code + # uses: actions/checkout@v2 + + # - name: Set up Node.js + # uses: actions/setup-node@v2 + # with: + # node-version: '16' # Change to your Node.js version + + # - name: Install dependencies + # run: | + # cd frontend + # yarn install + + # - name: Run Jest tests + # run: | + # cd frontend + # yarn test:e2e + From 1a57bbb94a59649fe4a920d79ab8164966a4a2b3 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 23:32:33 +0000 Subject: [PATCH 11/39] config: #2 define testing workflow --- .github/workflows/test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1e948a0..69d49dc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,8 +5,6 @@ on: push jobs: backend: runs-on: ubuntu-latest - defaults: - runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 From cc463eb38802c209f13cb21638bb3f37fe7bcfb0 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 23:41:28 +0000 Subject: [PATCH 12/39] config: #2 define testing workflow --- .github/workflows/test.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 69d49dc..be71219 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,17 +46,17 @@ jobs: cd frontend yarn test:unit - - name: Start Rails server - run: | - cd backend - bin/rails server -e test & # Run the server in the background - - - name: Wait for Rails server to be ready - run: | - until curl -s http://localhost:3000 > /dev/null; do - echo "Waiting for Rails server..." - sleep 2 - done + # - name: Start Rails server + # run: | + # cd backend + # bin/rails server -e test & # Run the server in the background + + # - name: Wait for Rails server to be ready + # run: | + # until curl -s http://localhost:3000 > /dev/null; do + # echo "Waiting for Rails server..." + # sleep 2 + # done - name: Run Playwright tests run: | From 2fba4c7873ba050c5c80c1d187a24ff77268a114 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 23:51:26 +0000 Subject: [PATCH 13/39] config: #2 define testing workflow --- .github/workflows/test.yml | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be71219..b1f7566 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -58,30 +58,20 @@ jobs: # sleep 2 # done + frontend-e2e: + runs-on: mcr.microsoft.com/playwright:v1.39.0-jammy + needs: [backend, frontend] + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install dependencies + run: | + cd frontend + yarn install + - name: Run Playwright tests run: | cd frontend yarn test:e2e - # frontend-e2e: - # runs-on: ubuntu-latest - # needs: backend # Ensure frontend tests run after backend tests - # steps: - # - name: Checkout code - # uses: actions/checkout@v2 - - # - name: Set up Node.js - # uses: actions/setup-node@v2 - # with: - # node-version: '16' # Change to your Node.js version - - # - name: Install dependencies - # run: | - # cd frontend - # yarn install - - # - name: Run Jest tests - # run: | - # cd frontend - # yarn test:e2e - From b6753485898b1c978acc8a305f2b5c338172bca0 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sat, 3 Aug 2024 23:59:05 +0000 Subject: [PATCH 14/39] config: #2 define testing workflow --- .github/workflows/test.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b1f7566..a5a2e62 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 @@ -29,10 +29,10 @@ jobs: needs: backend # Ensure frontend tests run after backend tests steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: '21' @@ -58,12 +58,20 @@ jobs: # sleep 2 # done - frontend-e2e: - runs-on: mcr.microsoft.com/playwright:v1.39.0-jammy + end-to-end: + name: 'Playwright Tests' + runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/playwright:v1.39.0-jammy needs: [backend, frontend] steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' - name: Install dependencies run: | @@ -72,6 +80,5 @@ jobs: - name: Run Playwright tests run: | - cd frontend yarn test:e2e From ec7a051180609a03a35f58a34f6ef1457a6e48f5 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sun, 4 Aug 2024 00:02:27 +0000 Subject: [PATCH 15/39] config: #2 define testing workflow --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a5a2e62..1a84ab9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,6 +4,7 @@ on: push jobs: backend: + name: 'Rails API Tests' runs-on: ubuntu-latest steps: - name: Checkout code @@ -25,6 +26,7 @@ jobs: bin/rails test frontend: + name: 'Vue Frontend Tests' runs-on: ubuntu-latest needs: backend # Ensure frontend tests run after backend tests steps: @@ -59,7 +61,7 @@ jobs: # done end-to-end: - name: 'Playwright Tests' + name: 'End to End Tests' runs-on: ubuntu-latest container: image: mcr.microsoft.com/playwright:v1.39.0-jammy @@ -80,5 +82,6 @@ jobs: - name: Run Playwright tests run: | + cd frontend yarn test:e2e From d0dbaa696829aac02753d6fa7dd1cbbc12367083 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sun, 4 Aug 2024 00:07:57 +0000 Subject: [PATCH 16/39] config: #2 define testing workflow --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1a84ab9..18497a2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -64,7 +64,7 @@ jobs: name: 'End to End Tests' runs-on: ubuntu-latest container: - image: mcr.microsoft.com/playwright:v1.39.0-jammy + image: mcr.microsoft.com/playwright:v1.45.1-jammy needs: [backend, frontend] steps: - name: Checkout code @@ -80,6 +80,9 @@ jobs: cd frontend yarn install + # - name: Install Playwright Browsers + # run: npx playwright install --with-deps + - name: Run Playwright tests run: | cd frontend From b34199a7b877aace0d0462a889f1e1467b7df38d Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sun, 4 Aug 2024 21:08:27 +0000 Subject: [PATCH 17/39] feat: #2 define mixin to manage company shifts info --- frontend/.env.api | 1 + frontend/.env.development | 1 + frontend/package.json | 2 + frontend/src/api/CompanyServiceApi.ts | 31 ++++++++-- frontend/src/api/types.ts | 2 - .../__tests__/useShiftManagement.spec.ts | 56 +++++++++++++++++++ frontend/src/mixins/useShiftManagement.ts | 28 ++++++++++ frontend/src/mock/company_services.json | 12 +--- 8 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 frontend/.env.api create mode 100644 frontend/.env.development create mode 100644 frontend/src/mixins/__tests__/useShiftManagement.spec.ts create mode 100644 frontend/src/mixins/useShiftManagement.ts diff --git a/frontend/.env.api b/frontend/.env.api new file mode 100644 index 0000000..19c5c2b --- /dev/null +++ b/frontend/.env.api @@ -0,0 +1 @@ +VUE_APP_USE_MOCK=false \ No newline at end of file diff --git a/frontend/.env.development b/frontend/.env.development new file mode 100644 index 0000000..1b4f468 --- /dev/null +++ b/frontend/.env.development @@ -0,0 +1 @@ +VUE_APP_USE_MOCK=true \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index a263659..382d172 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,6 +4,8 @@ "private": true, "scripts": { "serve": "vue-cli-service serve", + "serve:api": "vue-cli-service serve --mode api", + "dev": "vue-cli-service serve --mode development", "build": "vue-cli-service build", "test:unit": "vue-cli-service test:unit", "lint": "vue-cli-service lint", diff --git a/frontend/src/api/CompanyServiceApi.ts b/frontend/src/api/CompanyServiceApi.ts index c5240c4..d6b9215 100644 --- a/frontend/src/api/CompanyServiceApi.ts +++ b/frontend/src/api/CompanyServiceApi.ts @@ -1,6 +1,27 @@ -import { CompanyService } from "./types"; +import CompanyServices from "@/mock/company_services.json"; +import { CompanyService } from "@/api/types"; -export async function fetchCompanyServices(): Promise { - const response = await import("@/mock/company_services.json"); - return response.default.data; -} +const isMock = process.env.VUE_APP_USE_MOCK === "true"; + +export const fetchCompanyServices = async (): Promise => { + if (isMock) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(CompanyServices.data); + }, 500); + }); + } else { + try { + const response = await fetch("/api/company_services"); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const { data } = await response.json(); + return data; + } catch (error) { + console.error(error); + console.error("Failed to connect to the backend"); + throw new Error("Failed to connect to the backend"); + } + } +}; diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index edea817..b8717aa 100644 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -5,6 +5,4 @@ export interface ApiResponse { export interface CompanyService { id: number; name: string; - contract_start_date: string; - contract_end_date: string; } diff --git a/frontend/src/mixins/__tests__/useShiftManagement.spec.ts b/frontend/src/mixins/__tests__/useShiftManagement.spec.ts new file mode 100644 index 0000000..662e2da --- /dev/null +++ b/frontend/src/mixins/__tests__/useShiftManagement.spec.ts @@ -0,0 +1,56 @@ +import { ref } from "vue"; +import { useShiftManagement } from "@/mixins/useShiftManagement"; +import { fetchCompanyServices } from "@/api/CompanyServiceApi"; +import { CompanyService } from "@/api/types"; + +jest.mock("@/api/CompanyServiceApi"); + +describe("useShiftManagement", () => { + let services: { value: CompanyService[] }; + let selectedService: { value: number | null }; + let errorMessage: { value: string | null }; + + beforeEach(() => { + services = ref([]); + selectedService = ref(null); + errorMessage = ref(null); + + // Reset the mock implementation before each test + (fetchCompanyServices as jest.Mock).mockClear(); + }); + + it("initializes with empty company services and null selectedService", () => { + const { services, selectedService, errorMessage } = useShiftManagement(); + expect(services.value).toEqual([]); + expect(selectedService.value).toBe(null); + expect(errorMessage.value).toBe(null); + }); + + it("fetches services and updates the state", async () => { + (fetchCompanyServices as jest.Mock).mockResolvedValue([ + { id: 1, name: "Service A" }, + { id: 2, name: "Service B" }, + ]); + + const { services, fetchServices } = useShiftManagement(); + await fetchServices(); + + expect(services.value).toEqual([ + { id: 1, name: "Service A" }, + { id: 2, name: "Service B" }, + ]); + }); + + it("handles error when fetching services", async () => { + const errorMessageText = "Failed to connect to the backend"; + (fetchCompanyServices as jest.Mock).mockRejectedValue( + new Error(errorMessageText) + ); + + const { services, fetchServices, errorMessage } = useShiftManagement(); + await fetchServices(); + + expect(services.value).toEqual([]); + expect(errorMessage.value).toBe(errorMessageText); + }); +}); diff --git a/frontend/src/mixins/useShiftManagement.ts b/frontend/src/mixins/useShiftManagement.ts new file mode 100644 index 0000000..1c1feab --- /dev/null +++ b/frontend/src/mixins/useShiftManagement.ts @@ -0,0 +1,28 @@ +import { ref } from "vue"; + +import { CompanyService } from "@/api/types"; +import { fetchCompanyServices } from "@/api/CompanyServiceApi"; + +export function useShiftManagement() { + const services = ref([]); + const selectedService = ref(null); + const errorMessage = ref(null); + + const fetchServices = async () => { + try { + const data: CompanyService[] = await fetchCompanyServices(); + services.value = data; + errorMessage.value = null; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + errorMessage.value = error.message; + } + }; + + return { + services, + selectedService, + fetchServices, + errorMessage, + }; +} diff --git a/frontend/src/mock/company_services.json b/frontend/src/mock/company_services.json index 9ac307e..956e569 100644 --- a/frontend/src/mock/company_services.json +++ b/frontend/src/mock/company_services.json @@ -2,21 +2,15 @@ "data": [ { "id": 1, - "name": "Service A", - "contract_start_date": "2024-08-01T00:00:00Z", - "contract_end_date": "2024-12-31T23:59:59Z" + "name": "Service A" }, { "id": 2, - "name": "Service B", - "contract_start_date": "2024-07-01T00:00:00Z", - "contract_end_date": "2024-11-30T23:59:59Z" + "name": "Service B" }, { "id": 3, - "name": "Service C", - "contract_start_date": "2024-09-01T00:00:00Z", - "contract_end_date": "2025-01-31T23:59:59Z" + "name": "Service C" } ], "status": 200, From 96450b9fc11e19bcafe7ba1aa31f598866ae675f Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Sun, 4 Aug 2024 23:41:07 +0000 Subject: [PATCH 18/39] feat: #2 add shift management component and new CompanyServiceSelector that uses provided mixin --- frontend/src/App.vue | 3 ++ .../CompanyServiceSelector.vue | 39 ++++++++++++++++ .../__tests__/CompanyServiceSelector.page.ts | 45 +++++++++++++++++++ .../__tests__/CompanyServiceSelector.spec.ts | 44 ++++++++++++++++++ frontend/src/views/ShiftManagement.vue | 35 +++++++++++++++ 5 files changed, 166 insertions(+) create mode 100644 frontend/src/components/ShiftManagement/CompanyServiceSelector.vue create mode 100644 frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.page.ts create mode 100644 frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.spec.ts create mode 100644 frontend/src/views/ShiftManagement.vue diff --git a/frontend/src/App.vue b/frontend/src/App.vue index f243881..b882505 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,17 +1,20 @@ + + diff --git a/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.page.ts b/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.page.ts new file mode 100644 index 0000000..6244a6d --- /dev/null +++ b/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.page.ts @@ -0,0 +1,45 @@ +import { VueWrapper, mount } from "@vue/test-utils"; +import CompanyServiceSelector from "@/components/ShiftManagement/CompanyServiceSelector.vue"; +// import { nextTick } from "vue"; +import { CompanyService } from "@/api/types"; + +export class CompanyServiceSelectorPage { + private wrapper: VueWrapper; + //make fetchServices method from provided mixin public + public providerFetchServicesMock: jest.Mock; + + constructor( + services: CompanyService[] = [], + selectedService = { value: null as number | null } + ) { + const providerFetchServicesMock = jest.fn(); + this.wrapper = mount(CompanyServiceSelector, { + global: { + provide: { + shiftManagement: { + services, + selectedService, + fetchServices: providerFetchServicesMock, + }, + }, + }, + }); + this.providerFetchServicesMock = providerFetchServicesMock; + } + + get selectElement() { + return this.wrapper.find("select"); + } + + get options() { + return this.wrapper.findAll("option"); + } + + async selectService(serviceId: number | string) { + await this.selectElement.setValue(serviceId); + } + + get wrapperElement() { + return this.wrapper; + } +} diff --git a/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.spec.ts b/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.spec.ts new file mode 100644 index 0000000..d97d2c9 --- /dev/null +++ b/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.spec.ts @@ -0,0 +1,44 @@ +import { CompanyServiceSelectorPage } from "./CompanyServiceSelector.page"; +import { CompanyService } from "@/api/types"; + +describe("CompanyServiceSelector.vue", () => { + let page: CompanyServiceSelectorPage; + let providerServicesMock: CompanyService[]; + let providerSelectedServiceMock: { value: number | null }; + + beforeEach(() => { + // Mock Provided Mixin services + providerServicesMock = [ + { id: 1, name: "Service A" }, + { id: 2, name: "Service B" }, + ]; + + // Mock Provided Mixin selectedService + providerSelectedServiceMock = { value: null }; + + // Initialize the page object + page = new CompanyServiceSelectorPage( + providerServicesMock, + providerSelectedServiceMock + ); + }); + + it("renders the select element with available services", () => { + const options = page.options; + expect(options.length).toBe(3); + expect(options[0].text()).toBe("Select a service"); + expect(options[1].text()).toBe("Service A"); + expect(options[2].text()).toBe("Service B"); + }); + + it("selects a service and updates value", async () => { + await page.selectService(1); + expect(providerSelectedServiceMock.value).toBe(1); + }); + + it("calls fetchServices on mount", async () => { + const fetchServicesMock = page.providerFetchServicesMock; + // fetchServicesMock.mockImplementation(jest.fn()); + expect(fetchServicesMock).toHaveBeenCalled(); + }); +}); diff --git a/frontend/src/views/ShiftManagement.vue b/frontend/src/views/ShiftManagement.vue new file mode 100644 index 0000000..2aeac30 --- /dev/null +++ b/frontend/src/views/ShiftManagement.vue @@ -0,0 +1,35 @@ + + + + + From e9f4c5aa2e74bb957eae4dd781ce11a803cbfbaa Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Mon, 5 Aug 2024 00:49:35 +0000 Subject: [PATCH 19/39] feat: #2 add title to shiftManagement view --- frontend/src/App.vue | 3 --- frontend/src/views/ShiftManagement.vue | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index b882505..dbf2535 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,19 +1,16 @@ diff --git a/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.page.ts b/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.page.ts index 658763d..34889e3 100644 --- a/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.page.ts +++ b/frontend/src/components/ShiftManagement/__tests__/CompanyServiceSelector.page.ts @@ -1,6 +1,6 @@ import { VueWrapper, mount } from "@vue/test-utils"; -import CompanyServiceSelector from "@/components/ShiftManagement/CompanyServiceSelector.vue"; import { nextTick } from "vue"; +import CompanyServiceSelector from "@/components/ShiftManagement/CompanyServiceSelector.vue"; import { CompanyService } from "@/api/types"; export class CompanyServiceSelectorPage { @@ -19,10 +19,10 @@ export class CompanyServiceSelectorPage { provide: { shiftManagement: { services, - selectedService: { value: null }, fetchServices: this.providerFetchServicesMock, - fetchWeeks: this.providerFetchWeeksMock, selectService: this.providerSelectServiceMock, + selectedService: { value: null }, + fetchWeeks: this.providerFetchWeeksMock, }, }, }, diff --git a/frontend/src/components/ShiftManagement/__tests__/WeekSelector.page.ts b/frontend/src/components/ShiftManagement/__tests__/WeekSelector.page.ts new file mode 100644 index 0000000..6fb48fe --- /dev/null +++ b/frontend/src/components/ShiftManagement/__tests__/WeekSelector.page.ts @@ -0,0 +1,43 @@ +import { VueWrapper, mount } from "@vue/test-utils"; +import { nextTick } from "vue"; +import WeekSelector from "@/components/ShiftManagement/WeekSelector.vue"; +import { Weeks } from "@/api/types"; + +jest.mock("@/mixins/useShiftManagement"); + +export class WeekSelectorPage { + private wrapper: VueWrapper; + public providerFetchWeeksMock: jest.Mock; + public providerSelectWeekMock: jest.Mock; + + constructor(weeks: Weeks) { + this.providerFetchWeeksMock = jest.fn(); + this.providerSelectWeekMock = jest.fn(); + + this.wrapper = mount(WeekSelector, { + global: { + provide: { + shiftManagement: { + pastWeeks: weeks.past, + futureWeeks: weeks.future, + selectWeek: this.providerSelectWeekMock, + fetchWeeks: this.providerFetchWeeksMock, + }, + }, + }, + }); + } + + get selectElement() { + return this.wrapper.find("select"); + } + + get options() { + return this.wrapper.findAll("option"); + } + + async selectWeek(weekId: string) { + await this.selectElement.setValue(weekId); + await nextTick(); + } +} diff --git a/frontend/src/components/ShiftManagement/__tests__/WeekSelector.spec.ts b/frontend/src/components/ShiftManagement/__tests__/WeekSelector.spec.ts new file mode 100644 index 0000000..682b914 --- /dev/null +++ b/frontend/src/components/ShiftManagement/__tests__/WeekSelector.spec.ts @@ -0,0 +1,59 @@ +import { WeekSelectorPage } from "./WeekSelector.page"; +import { Weeks } from "@/api/types"; + +describe("WeekSelector.vue", () => { + let page: WeekSelectorPage; + let providerWeeksMock: Weeks; + + beforeEach(() => { + providerWeeksMock = { + future: [ + { + id: "2024-32", + label: "Semana 32 del 2024", + start_date: "05/08/2024", + end_date: "11/08/2024", + }, + { + id: "2024-33", + label: "Semana 33 del 2024", + start_date: "12/08/2024", + end_date: "18/08/2024", + }, + ], + past: [ + { + id: "2024-30", + label: "Semana 30 del 2024", + start_date: "22/07/2024", + end_date: "28/07/2024", + }, + { + id: "2024-31", + label: "Semana 31 del 2024", + start_date: "29/07/2024", + end_date: "04/08/2024", + }, + ], + }; + + page = new WeekSelectorPage(providerWeeksMock); + }); + + it("renders the select element with available weeks", () => { + const options = page.options; + expect(options.length).toBe(5); // Includes the default option + expect(options[0].text()).toBe("Semana"); + expect(options[1].text()).toBe("Semana 32 del 2024"); + expect(options[2].text()).toBe("Semana 33 del 2024"); + expect(options[3].text()).toBe("Semana 30 del 2024"); + expect(options[4].text()).toBe("Semana 31 del 2024"); + expect(options[3].classes()).toContain("bg-gray-400"); + expect(options[4].classes()).toContain("bg-gray-400"); + }); + + it("selects a week and updates the selected week value", async () => { + await page.selectWeek("2024-32"); + expect(page.providerSelectWeekMock).toHaveBeenCalledWith("2024-32"); + }); +}); diff --git a/frontend/src/views/ShiftManagement.vue b/frontend/src/views/ShiftManagement.vue index ba050d3..ae8eb2c 100644 --- a/frontend/src/views/ShiftManagement.vue +++ b/frontend/src/views/ShiftManagement.vue @@ -3,11 +3,9 @@

Company Service Shifts

+ +
{{ dateRange }}
-
{{ errorMessage }}
Service ID: {{ selectedService }}
@@ -17,9 +15,7 @@ import { provide, onMounted } from "vue"; import { useShiftManagement } from "@/mixins/useShiftManagement"; import CompanyServiceSelector from "@/components/ShiftManagement/CompanyServiceSelector.vue"; -// import WeekSelector from "@/components/ShiftManagement/WeekSelector.vue"; -// import EngineerList from "@/components/ShiftManagement/EngineerList.vue"; -// import ShiftTable from "@/components/ShiftManagement/ShiftTable.vue"; +import WeekSelector from "@/components/ShiftManagement/WeekSelector.vue"; const shiftManagement = useShiftManagement(); @@ -30,7 +26,7 @@ onMounted(() => { shiftManagement.fetchServices(); }); -const { errorMessage, selectedService } = shiftManagement; +const { errorMessage, selectedService, dateRange } = shiftManagement; From 25426b522be1c7d2e1570abb0435e7a93c9552cf Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Mon, 5 Aug 2024 22:26:00 +0000 Subject: [PATCH 28/39] feat: #2 define api and mocks for shifts grid --- frontend/src/api/CompanyServiceApi.ts | 36 ++++++- frontend/src/api/types.ts | 23 ++++ frontend/src/mock/shifts_a_w1.json | 149 ++++++++++++++++++++++++++ frontend/src/mock/shifts_b_w1.json | 149 ++++++++++++++++++++++++++ 4 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 frontend/src/mock/shifts_a_w1.json create mode 100644 frontend/src/mock/shifts_b_w1.json diff --git a/frontend/src/api/CompanyServiceApi.ts b/frontend/src/api/CompanyServiceApi.ts index 78a823c..078238e 100644 --- a/frontend/src/api/CompanyServiceApi.ts +++ b/frontend/src/api/CompanyServiceApi.ts @@ -1,7 +1,9 @@ import CompanyServices from "@/mock/company_services.json"; import WeeksServiceA from "@/mock/weeks_service_a.json"; import WeeksServiceB from "@/mock/weeks_service_b.json"; -import { CompanyService, Weeks } from "@/api/types"; +import ShiftsServiceAWeek1 from "@/mock/shifts_a_w1.json"; +import ShiftsServiceBWeek1 from "@/mock/shifts_b_w1.json"; +import { CompanyService, Weeks, Shift } from "@/api/types"; const isMock = process.env.VUE_APP_USE_MOCK === "true"; @@ -54,3 +56,35 @@ export const requestWeeks = async (serviceId: number): Promise => { } } }; + +export const requestShifts = async ( + serviceId: number, + weekId: string +): Promise => { + if (isMock) { + return new Promise((resolve) => { + setTimeout(() => { + if (serviceId === 1) { + resolve(ShiftsServiceAWeek1.data); + } else { + resolve(ShiftsServiceBWeek1.data); + } + }, 500); + }); + } else { + try { + const response = await fetch( + `/api/company_services/${serviceId}/shifts?week=${weekId}` + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const { data } = await response.json(); + return data; + } catch (error) { + console.error(error); + console.error("Failed to fetch shifts"); + throw new Error("Failed to fetch shifts"); + } + } +}; diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index e31b13e..7dba260 100644 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -38,3 +38,26 @@ export type WeeksHash = { end_date: string; }; }; + +export interface ShiftsResponse { + data: Shift[]; + status: number; + statusText: string; +} + +export interface Shift { + day: string; + time_blocks: Timeblock[]; +} + +export interface Timeblock { + start_time: string; + end_time: string; + amount_of_hours: number; + engineer: Engineer | null; +} + +export interface Engineer { + id: number; + name: string; +} diff --git a/frontend/src/mock/shifts_a_w1.json b/frontend/src/mock/shifts_a_w1.json new file mode 100644 index 0000000..f2db338 --- /dev/null +++ b/frontend/src/mock/shifts_a_w1.json @@ -0,0 +1,149 @@ +{ + "data": [ + { + "day": "Monday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": { + "id": 1, + "name": "Engineer 1" + } + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": { + "id": 2, + "name": "Engineer 2" + } + } + ] + }, + { + "day": "Tuesday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": null + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": { + "id": 3, + "name": "Engineer 3" + } + } + ] + }, + { + "day": "Wednesday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": { + "id": 1, + "name": "Engineer 1" + } + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": null + } + ] + }, + { + "day": "Thursday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": { + "id": 2, + "name": "Engineer 2" + } + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": null + } + ] + }, + { + "day": "Friday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": null + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": { + "id": 3, + "name": "Engineer 3" + } + } + ] + }, + { + "day": "Saturday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": { + "id": 1, + "name": "Engineer 1" + } + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": null + } + ] + }, + { + "day": "Sunday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": null + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": { + "id": 2, + "name": "Engineer 2" + } + } + ] + } + ], + "status": 200, + "statusText": "OK" +} diff --git a/frontend/src/mock/shifts_b_w1.json b/frontend/src/mock/shifts_b_w1.json new file mode 100644 index 0000000..55fe16e --- /dev/null +++ b/frontend/src/mock/shifts_b_w1.json @@ -0,0 +1,149 @@ +{ + "data": [ + { + "day": "Monday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": { + "id": 4, + "name": "Engineer 4" + } + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": { + "id": 5, + "name": "Engineer 5" + } + } + ] + }, + { + "day": "Tuesday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": null + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": { + "id": 6, + "name": "Engineer 6" + } + } + ] + }, + { + "day": "Wednesday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": { + "id": 4, + "name": "Engineer 4" + } + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": null + } + ] + }, + { + "day": "Thursday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": { + "id": 5, + "name": "Engineer 5" + } + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": null + } + ] + }, + { + "day": "Friday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": null + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": { + "id": 6, + "name": "Engineer 6" + } + } + ] + }, + { + "day": "Saturday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": { + "id": 4, + "name": "Engineer 4" + } + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": null + } + ] + }, + { + "day": "Sunday", + "time_blocks": [ + { + "start_time": "09:00", + "end_time": "10:00", + "amount_of_hours": 1, + "engineer": null + }, + { + "start_time": "10:00", + "end_time": "11:00", + "amount_of_hours": 1, + "engineer": { + "id": 5, + "name": "Engineer 5" + } + } + ] + } + ], + "status": 200, + "statusText": "OK" +} From 79804e3c048506bcfefdf6296b848c3b3ac115f9 Mon Sep 17 00:00:00 2001 From: adrian peralta Date: Mon, 5 Aug 2024 23:01:46 +0000 Subject: [PATCH 29/39] feat: #2 add aria label to improve accessibility and e2e tests --- frontend/e2e/checkCompanyServiceShifts.spec.js | 15 +++++++++------ frontend/package.json | 3 ++- .../ShiftManagement/CompanyServiceSelector.vue | 10 ++++++++-- .../components/ShiftManagement/WeekSelector.vue | 8 +++++++- .../__tests__/CompanyServiceSelector.spec.ts | 2 +- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/frontend/e2e/checkCompanyServiceShifts.spec.js b/frontend/e2e/checkCompanyServiceShifts.spec.js index f79ed8e..ac04d98 100644 --- a/frontend/e2e/checkCompanyServiceShifts.spec.js +++ b/frontend/e2e/checkCompanyServiceShifts.spec.js @@ -13,14 +13,17 @@ test.describe("Check Company Service Shifts", () => { await page.goto("/"); // Select a service - await page.selectOption("select#companyService", "Service A"); + await page.selectOption('select[aria-label="Selecciona un Servicio"]', { + label: "Service A", + }); + // Select a week - await page.selectOption("select#week", "Week 32 in 2024"); + await page.selectOption('select[aria-label="Selecciona una Semana"]', { + label: "Semana 32 del 2024", + }); // Show Subtitle - const weekRangeDatesTitle = await page.locator("h2"); - await expect(weekRangeDatesTitle).toHaveText( - "From 04/08/2024 to 10/08/2024" - ); + const dateRange = await page.locator("text=Del 05/08/2024 al 11/08/2024"); + await expect(dateRange).toBeVisible(); }); }); diff --git a/frontend/package.json b/frontend/package.json index 382d172..00db767 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,7 +9,8 @@ "build": "vue-cli-service build", "test:unit": "vue-cli-service test:unit", "lint": "vue-cli-service lint", - "test:e2e": "start-server-and-test serve http://localhost:8080 test:playwright", + "test:e2e": "start-server-and-test dev http://localhost:8080 test:playwright", + "test:e2e:api": "start-server-and-test serve:api http://localhost:8080 test:playwright", "test:playwright": "playwright test" }, "dependencies": { diff --git a/frontend/src/components/ShiftManagement/CompanyServiceSelector.vue b/frontend/src/components/ShiftManagement/CompanyServiceSelector.vue index 88531c3..12088fa 100644 --- a/frontend/src/components/ShiftManagement/CompanyServiceSelector.vue +++ b/frontend/src/components/ShiftManagement/CompanyServiceSelector.vue @@ -1,10 +1,16 @@