Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug server-side",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev"
},
{
"name": "Next.js: debug client-side",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000"
},
{
"name": "Next.js: debug full stack",
"type": "node-terminal",
"request": "launch",
"command": "yarn dev",
"serverReadyAction": {
"pattern": "- Local:.+(https?://.+)",
"uriFormat": "%s",
"action": "debugWithChrome"
}
}
]
}
52 changes: 41 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,59 @@
# Virgin Atlantic / Virgin Atlantic Holidays ~ Holiday Search Results (Front-end test)

## Application screens

![Alt Text](./im1.JPG)
![Alt Text](./im2.JPG)
![Alt Text](./im3.JPG)
Thank you for your interest in joining our front-end team and taking the time to do the test. We think it will provide a nice and interesting challenge and a good talking point for the next stage of the process.

## Rules

You must write the application in [React](https://react.dev/) and [TypeScript](https://www.typescriptlang.org/) with [NextJS](https://nextjs.org/). This is all set up and ready to use inside this project.

We have used [new App Router](https://nextjs.org/docs/app/building-your-application/routing) in Next to make the test a bit more interesting and mix rendering on the client and server.

For the test you should adhere to the following conditions:

1) The code must be your own work. If you have a strong case to use a small code snippet of someone else's e.g. a
boilerplate function, it must be clearly commented and attributed to the original author
1) You must include any unit tests you think are appropriate - Cypress is set up for you already
1) Give consideration to performance and accessibility. Lighthouse scores are high - please keep them that way
1) Code must be clear, concise and human readable
1) We do not want to see how well you can use a UI framework. This is a test and we want to see what you can create so please avoid Bootstrap, Material UI etc
1) Simple CSS has been included but please use whatever CSS flavour you like: LESS, Emotion, Styled Components etc
1. The code must be your own work. If you have a strong case to use a small code snippet of someone else's e.g. a
boilerplate function, it must be clearly commented and attributed to the original author
1. You must include any unit tests you think are appropriate - Cypress is set up for you already
1. Give consideration to performance and accessibility. Lighthouse scores are high - please keep them that way
1. Code must be clear, concise and human readable
1. We do not want to see how well you can use a UI framework. This is a test and we want to see what you can create so please avoid Bootstrap, Material UI etc
1. Simple CSS has been included but please use whatever CSS flavour you like: LESS, Emotion, Styled Components etc

## Pull Request Explanation

In this pull request, I have implemented the search results page for the front-end test. Here are the key changes made:

1. Created a new component called `SearchResultsComponent` which connects to the holiday search API to fetch and display a list of holidays based on the given location and departure date.

2. Added filters for price per person, hotel facilities, and star rating to allow users to refine their search results.

3. Implemented the API call inside the `SearchResultsComponent` and handled the loading state while waiting for the results to be fetched.

4. Utilized the provided typings in `src/types/booking.ts` to ensure type safety when handling the API response.

5. Included appropriate unit tests using Cypress to ensure the functionality of the search results page.

6. Ensured performance and accessibility considerations by maintaining high Lighthouse scores.

7. Used React, TypeScript, and NextJS as specified in the test requirements.

Please review the code changes in the public GitHub repository: [link-to-repository](https://github.com/your-username/your-repository)

Thank you for your time and consideration. I look forward to your feedback.

## What it should do
Build the 'search results page' which connects to our holiday search API to display a list of holidays for a given location and departure date. Select what data items (example listed below) you think should be included on the page. You can use the [live website](https://www.virginholidays.co.uk) and a holiday search as an example but feel free to change things up.

Build the 'search results page' which connects to our holiday search API to display a list of holidays for a given location and departure date. Select what data items (example listed below) you think should be included on the page. You can use the [live website](https://www.virginholidays.co.uk) and a holiday search as an example but feel free to change things up.

Add the ability to filter the results by:

1) Price per person
1) Hotel facilities
1) Star rating
1. Price per person
1. Hotel facilities
1. Star rating

The call to the API has been made inside the SearchResultsComponent and some example links are provided on the home route.

Expand All @@ -35,6 +64,7 @@ The endpoint will return data relating to the holiday packages available. The ke
Results can be slow to load so you will need to handle this in your code how ever you wish but a basic `loading.tsx` file has been added.

## Supplying your code

Please **create and commit your code into a public Github repository** and supply the link to the recruiter for review.

Thanks for your time, we look forward to hearing from you!
Expand Down
48 changes: 48 additions & 0 deletions cypress/component/hotel-widget.component.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import HotelWidgetComponent from "@/app/components/search-results/hotel-widget.component";

describe('HotelWidgetComponent', () => {
it('renders hotel widget component correctly', () => {
// lets mock the props
const widgetParams = {
hotel: {
content: {
starRating: 4,
images: [{
RESULTS_CAROUSEL: {
url: "//d3hk78fplavsbl.cloudfront.net/assets/common-prod/hotel/205/8271/8271-1-results_carousel.jpg?version=33"
}
},
{
RESULTS_CAROUSEL: {
url: "//d3hk78fplavsbl.cloudfront.net/assets/common-prod/hotel/205/8271/8271-2-results_carousel.jpg?version=33"
}
}
]
},
name: 'Hotel ABC',
tripAdvisor: {
numReviews: 100,
},
boardBasis: 'All Inclusive',
salesMessage: {
header: 'Special Offer',
},
},
totalPrice: 1000,
pricePerPerson: 250,
};
const guests = ['Suneel', 'Tanmay'];

// Mount the component
cy.mount(<HotelWidgetComponent widgetParams={widgetParams} guests={guests} />);
// Assert the rendered content
cy.get('h1').contains('Hotel ABC');
cy.get('button').contains('Find details');
cy.get('button').contains('Hotel details');
cy.get('button').contains('Reviews');
cy.get('p').contains('Based on 100 reviews');
cy.contains('Special Offer')
.should('have.length', 1)
.and('have.text', 'Special Offer');
});
});
239 changes: 239 additions & 0 deletions cypress/utils/filterData.service.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import { generateFilterParametrs, filterHotelData } from "@/utils/filterData.service";
describe('generateFilterParametrs', () => {
it('should generate filter parameters correctly', () => {
// Test data
const results = {
holidays: [
{
pricePerPerson: 100,
totalPrice: 200,
hotel: {
boardBasis: 'BB',
content: {
starRating: 4,
hotelFacilities: ['Pool', 'Gym'],
name: 'Hotel 1'
}
}
},
{
pricePerPerson: 200,
totalPrice: 400,
hotel: {
boardBasis: 'Bed and Breakfast',
content: {
starRating: 5,
hotelFacilities: ['Pool', 'Gym'],
name: 'Hotel 2'
}
}
}]
};

// Expected result
const expectedFilterParams = {
data: [
{
filterName: 'Hotel Name',
filterId: 'hotelName',
filters: [
{
filterType: 'input',
filterId: 'hotelName'
}
]
},
{
filterName: 'Star Ratings',
filterId: 'ratings',
filters: [
{
filterType: 'checkbox',
filterId: 4,
value: 4,
label: '4(1)'
},
{
filterType: 'checkbox',
filterId: 5,
value: 5,
label: '5(1)'
},
{
filterType: 'checkbox',
filterId: 'NA',
value: 'NA',
label: 'Not Rated(0)'
}
]
},
{
interval: 1000,
filterName: 'Price(PP)',
filterId: 'pricePP',
filters: [
{
filterType: 'checkbox',
filterId: 'upto100',
value: 100,
min: 0,
max: 100,
label: 'upto £100(1)'
},
{
filterType: 'checkbox',
filterId: '100-1100',
value: 1100,
min: 100,
max: 1100,
label: '£100 to £1100(2)'
}
]
},
{
filterName: 'Hotel Facilities',
filterId: 'hotelFacilities',
filters: [
{
filterType: 'checkbox',
filterId: 'Gym',
value: 'Gym',
label: 'Gym(2)'
},
{
filterType: 'checkbox',
filterId: 'Pool',
value: 'Pool',
label: 'Pool(2)'
}
]
}
]
};

// Generate filter parameters
const filterParams = generateFilterParametrs(results);
console.log('suneel', filterParams, expectedFilterParams);
// Assertion
expect(filterParams).to.deep.equal(expectedFilterParams);
});
});

describe('filterHotelData', () => {
// Test data
const holidays = [
{
pricePerPerson: 100,
hotel: {
content: {
starRating: 4,
hotelFacilities: ['Pool', 'Gym'],
name: 'Hotel 1'
}
}
},
{
pricePerPerson: 200,
hotel: {
content: {
starRating: 5,
hotelFacilities: ['Spa', 'Restaurant'],
name: 'Hotel 2'
}
}
}
];

it('should filter hotel data based on ratings', () => {
// Filter parameters
const filterParams = {
ratings: [
{ value: 4 },
{ value: 5 }
]
};

// Expected result
const expectedFilteredData = holidays;

// Filter hotel data
const filteredData = filterHotelData(holidays, filterParams);

// Assertion
expect(filteredData).to.deep.equal(expectedFilteredData);
});

it('should filter hotel data based on hotel facilities', () => {
// Filter parameters
const filterParams = {
hotelFacilities: [
{ value: 'Spa' }
]
};

// Expected result
const expectedFilteredData = [
{
pricePerPerson: 200,
hotel: {
content: {
starRating: 5,
hotelFacilities: ['Spa', 'Restaurant'],
name: 'Hotel 2'
}
}
}
];

// Filter hotel data
const filteredData = filterHotelData(holidays, filterParams);

// Assertion
expect(filteredData).to.deep.equal(expectedFilteredData);
});

it('should filter hotel data based on price per person', () => {
// Filter parameters
const filterParams = {
pricePP: [
{ min: 0, max: 100 }
]
};

// Expected result
const expectedFilteredData = [
{
pricePerPerson: 100,
hotel: {
content: {
starRating: 4,
hotelFacilities: ['Pool', 'Gym'],
name: 'Hotel 1'
}
}
}
];

// Filter hotel data
const filteredData = filterHotelData(holidays, filterParams);

// Assertion
expect(filteredData).to.deep.equal(expectedFilteredData);
});

it('should filter hotel data based on hotel name', () => {
// Filter parameters
const filterParams = {
hotelName: 'hotel'
};

// Expected result
const expectedFilteredData = holidays;

// Filter hotel data
const filteredData = filterHotelData(holidays, filterParams);

// Assertion
expect(filteredData).to.deep.equal(expectedFilteredData);
});
});
Binary file added im1.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added im2.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added im3.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading