diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 50b5ec1..ccffc6c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,4 +17,10 @@ updates: directory: "/" # Check for updates once a week schedule: - interval: "weekly" \ No newline at end of file + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "daily" \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4c2cef3..edd45be 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,7 +6,7 @@ on: - cron: '0 3 * * *' # Runs nightly at 3 AM UTC jobs: - build-and-test: + run-tests: runs-on: ubuntu-latest steps: @@ -18,7 +18,6 @@ jobs: with: node-version: 22 - # 1) Install Chrome so Selenium can run - name: Install Chrome run: | sudo apt-get update @@ -27,11 +26,9 @@ jobs: - name: Install dependencies run: npm install - # 2) Spin up DynamoDB local on port 8000 - name: Start DynamoDB Local run: docker run -d -p 8000:8000 amazon/dynamodb-local - # 3) Set environment vars so the AWS SDK points to local DynamoDB - name: Set up env vars run: | echo "AWS_ACCESS_KEY_ID=FAKE_KEY" >> $GITHUB_ENV @@ -40,21 +37,18 @@ jobs: echo "DYNAMODB_ENDPOINT=http://127.0.0.1:8000" >> $GITHUB_ENV echo "DYNAMODB_TABLE_NAME=RoomsTest" >> $GITHUB_ENV # IMPORTANT: Make sure this matches the port your Express app uses - # If your app listens on 8081, set this to http://localhost:8081 - echo "BASE_URL=http://localhost:3000" >> $GITHUB_ENV + echo "BASE_URL=http://localhost:8081" >> $GITHUB_ENV + echo "CI=true" >> $GITHUB_ENV + - # 4) Start your Express app in the background - # By default your "npm start" runs on port 3000 or 8081—adjust sleep time as necessary - name: Start server run: | npm start & # Give the server a few seconds to spin up before running tests sleep 5 - # 5) Run your Jest-based unit tests - name: Run unit tests run: npm run unit-test - # 6) Run your Selenium + Cucumber integration tests - name: Run integration tests run: npm run integration-tests diff --git a/test/integration-tests/features/step_definitions/hotel_steps.js b/test/integration-tests/features/step_definitions/hotel_steps.js index d921e8c..8bad745 100644 --- a/test/integration-tests/features/step_definitions/hotel_steps.js +++ b/test/integration-tests/features/step_definitions/hotel_steps.js @@ -1,8 +1,21 @@ const { setDefaultTimeout, Given, When, Then, After } = require('@cucumber/cucumber'); const { Builder, By, until } = require('selenium-webdriver'); +const chrome = require('selenium-webdriver/chrome'); // <-- Add this import const { openHomepage, verifyHomepageElements } = require('./helpers/homepageHelper'); -const { openAddRoomPage, fillAddRoomForm, verifyAddRoomSuccess, verifyAddRoomFormFields, verifySubmitButton } = require('./helpers/addRoomHelper'); -const { openRoomsPage, verifyRoomList, verifyRoomDetails, verifyTableColumns, verifyRoomsStoredAlert } = require('./helpers/roomsHelper'); +const { + openAddRoomPage, + fillAddRoomForm, + verifyAddRoomSuccess, + verifyAddRoomFormFields, + verifySubmitButton +} = require('./helpers/addRoomHelper'); +const { + openRoomsPage, + verifyRoomList, + verifyRoomDetails, + verifyTableColumns, + verifyRoomsStoredAlert +} = require('./helpers/roomsHelper'); let driver; @@ -10,106 +23,120 @@ setDefaultTimeout(120 * 1000); // needed for the time it takes to spin up the re // Function to build and return a WebDriver instance const buildDriver = async () => { - const gridUrl = process.env.GRID_URL || null; - - if (gridUrl) { - console.log(`Using Selenium Grid at: ${gridUrl}`); - driver = await new Builder() - .usingServer(gridUrl) // Use the Selenium Grid URL here - .forBrowser('chrome') - .build(); - } else { - // If no GRID_URL is set, run the browser locally - driver = await new Builder().forBrowser('chrome').build(); + const gridUrl = process.env.GRID_URL || null; + + if (gridUrl) { + console.log(`Using Selenium Grid at: ${gridUrl}`); + driver = await new Builder() + .usingServer(gridUrl) // Use the Selenium Grid URL here + .forBrowser('chrome') + .build(); + } else { + // If no GRID_URL is set, run the browser locally + let options = new chrome.Options(); + + if (process.env.CI) { + // Running in CI: add headless flags + options = options + .headless() + .addArguments('--disable-gpu') + .addArguments('--no-sandbox') + .addArguments('--disable-dev-shm-usage'); } - // Common settings for the driver - await driver.manage().setTimeouts({ implicit: 10000 }); - await driver.manage().window().setRect({ width: 1920, height: 1080 }); // Full HD resolution + driver = await new Builder() + .forBrowser('chrome') + .setChromeOptions(options) + .build(); + } + + // Common settings for the driver + await driver.manage().setTimeouts({ implicit: 10000 }); + await driver.manage().window().setRect({ width: 1920, height: 1080 }); // Full HD resolution }; // Step Definitions Given('I am on the homepage', async function () { - await buildDriver(); // Create WebDriver instance - const baseUrl = process.env.BASE_URL || 'http://localhost:3000'; - await openHomepage(driver, baseUrl); // Use helper function to open the homepage + await buildDriver(); // Create WebDriver instance + const baseUrl = process.env.BASE_URL || 'http://localhost:3000'; + await openHomepage(driver, baseUrl); // Use helper function to open the homepage }); Then('I should see the page title {string}', async function (title) { - await verifyHomepageElements(driver, 'title', title); // Use helper function to verify the title + await verifyHomepageElements(driver, 'title', title); // Use helper function to verify the title }); Then('I should see a navbar with {string}, {string}, and {string} options', async function (home, rooms, add) { - await verifyHomepageElements(driver, 'navbar'); // Use helper function to verify the navbar with the given options + await verifyHomepageElements(driver, 'navbar'); // Use helper function to verify the navbar }); Then('I should see the heading {string}', async function (headingText) { - await verifyHomepageElements(driver, 'heading', headingText); // Use helper function to verify the heading + await verifyHomepageElements(driver, 'heading', headingText); // Use helper function to verify the heading }); When('I click on {string} in the navbar', async function (linkText) { - if (linkText === "Rooms") { - await openRoomsPage(driver); // Use helper to open the Rooms page - } else if (linkText === "Add") { - await openAddRoomPage(driver); // Use helper to open the Add Room page - } else { - throw new Error(`Link text ${linkText} is not supported.`); - } + if (linkText === "Rooms") { + await openRoomsPage(driver); // Use helper to open the Rooms page + } else if (linkText === "Add") { + await openAddRoomPage(driver); // Use helper to open the Add Room page + } else { + throw new Error(`Link text ${linkText} is not supported.`); + } }); Then('I should be on the {string} page', async function (pageTitle) { - await verifyRoomList(driver, 'title', pageTitle); // Use helper function to verify the page title + await verifyRoomList(driver, 'title', pageTitle); // Use helper function to verify the page title }); Then('I should see a table with the list of rooms', async function () { - await verifyRoomList(driver, 'room_table'); // Use helper function to verify room table is present + await verifyRoomList(driver, 'room_table'); // Use helper function to verify room table is present }); Then('the table should contain columns for {string}, {string}, and {string}', async function (column1, column2, column3) { - await verifyTableColumns(driver); // Use helper function to verify table columns + await verifyTableColumns(driver); // Use helper function to verify table columns }); When('I enter {string} in the {string} field', async function (value, fieldName) { - if (fieldName === "Room number") { - await fillAddRoomForm(driver, "room_number", value); - } else if (fieldName === "Floor number") { - await fillAddRoomForm(driver, "floor_number", value); - } else { - throw new Error(`Unsupported field name: ${fieldName}`); - } + if (fieldName === "Room number") { + await fillAddRoomForm(driver, "room_number", value); + } else if (fieldName === "Floor number") { + await fillAddRoomForm(driver, "floor_number", value); + } else { + throw new Error(`Unsupported field name: ${fieldName}`); + } }); When('I select {string} from the "Good View" dropdown', async function (value) { - await fillAddRoomForm(driver, "good_view", value); + await fillAddRoomForm(driver, "good_view", value); }); When('I click the "Add room" button', async function () { - await fillAddRoomForm(driver, "submit"); + await fillAddRoomForm(driver, "submit"); }); Then('the new room should be added successfully', async function () { - await verifyAddRoomSuccess(driver); + await verifyAddRoomSuccess(driver); }); Then('I should see a room with the room number {string}, on floor {string}, with {string} under Good View', async function (roomNumber, floorNumber, viewStatus) { - await verifyRoomDetails(driver, roomNumber, floorNumber, viewStatus); + await verifyRoomDetails(driver, roomNumber, floorNumber, viewStatus); }); Then('I should see an alert displaying the number of rooms stored in the database', async function () { - await verifyRoomsStoredAlert(driver); + await verifyRoomsStoredAlert(driver); }); Then('I should see a form with fields for {string}, {string}, and {string}', { timeout: 20000 }, async function (field1, field2, field3) { - await verifyAddRoomFormFields(driver); + await verifyAddRoomFormFields(driver); }); Then('I should see a submit button labeled {string}', { timeout: 20000 }, async function (buttonLabel) { - await verifySubmitButton(driver, buttonLabel); + await verifySubmitButton(driver, buttonLabel); }); // Tear Down After(async function () { - if (driver) { - await driver.quit(); - } + if (driver) { + await driver.quit(); + } });