Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup E2E testing Infrastructure #188

Open
wants to merge 13 commits into
base: next
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ appspec.yml
logs
aws
.DS_Store

packages/mock-app/src/constants/app-config.json
packages/components/src/constants/app-config.json
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ RUN yarn build

ARG CONFIG_FILE
COPY ${CONFIG_FILE} packages/mock-app/src/constants/app-config.json
COPY ${CONFIG_FILE} packages/components/src/constants/app-config.json
WORKDIR /app/packages/mock-app
RUN yarn build

Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,16 @@ export MOCK_GS1_SERVICE_API_KEY=test456 # Mock GS1 service API key
# Run seeding scripts
./seeding/idr-data.sh
./seeding/mock-gs1-data.sh
```

## End-to-end testing
```bash
# Run end-to-end testing scripts
yarn build-clean # clean node_modules before setup
yarn install
yarn build
SEEDING=true docker compose -f docker-compose.e2e.yml up -d
chmod +x run-e2e-tests.sh
./run-e2e-tests.sh
yarn cypress run
```
7,079 changes: 7,079 additions & 0 deletions app-config.e2e.json

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { defineConfig } from 'cypress';
import fs from 'fs';
import path from 'path';
import { exec } from 'child_process';
import util from 'util';

const execPromise = util.promisify(exec);
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3003', // Replace with your application's base URL
supportFile: false, // Disable the default support file if not needed
specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', // Specifies the test file pattern
video: false, // Disable video recording (optional)
chromeWebSecurity: false, // Helps bypass security restrictions (if needed)
retries: {
runMode: 2, // Retries in headless mode
openMode: 0, // No retries in interactive mode
},
defaultCommandTimeout: 4000,
defaultBrowser: "chrome",
setupNodeEvents(on) {
on('task', {
writeToFile({ fileName, data }: { fileName: string; data: any }) {
const filePath = path.resolve('cypress/fixtures/credentials-e2e', fileName);
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
return null;
},
async runShellScript({ scriptPath }: { scriptPath: string }) {
const absolutePath = path.resolve(process.cwd(), scriptPath);
try {
const { stdout } = await execPromise(`bash ${absolutePath}`);
return stdout;
} catch (error: any) {
throw error;
}
}
});
},
},
});
54 changes: 54 additions & 0 deletions cypress/e2e/actor_content_test/actorName.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
describe('Facility Links Test', () => {
it('should visit the homepage and verify the facility links', () => {
// Visit the homepage
cy.visit('/');
// Verify the presence of the "CHERRIES SUPPLY CHAIN TRACEABILITY" text
cy.contains('h5', 'CHERRIES SUPPLY CHAIN TRACEABILITY').should('be.visible');
});

it('should verify all <a> tags from the second match the expected texts', () => {
const expectedTexts = [
'Orchard Facility',
'Packhouse Facility',
'Fumigation and Freight Forwarding Facility',
'Airport Terminal Facility',
'Scanning',
'General features',
];

cy.visit('/');

cy.get('a').then(($aTags) => {
expect($aTags.length - 1).to.equal(expectedTexts.length);

$aTags.each((index, el) => {
if (index === 0) return;

cy.wrap(el)
.invoke('text')
.then((text) => {
expect(text.trim()).to.equal(expectedTexts[index - 1]);
});
});
});
});

describe('Facility Links Test', () => {
it('should visit the homepage and click on the "Orchard Facility" link', () => {
// Visit the homepage
cy.visit('/');

// Locate the Orchard Facility link by its href attribute and class
cy.get('a').eq(1).should('be.visible').and('not.be.disabled').click()

// Verify the URL to confirm navigation
cy.url().should('include', '/orchard-facility');
// Check if the Submit button appears after clicking
cy.get('a') // Adjust this selector if the button isn't of type submit
.eq(1)
.should('be.visible')
.and('not.be.disabled')
.click()
});
});
});
10 changes: 10 additions & 0 deletions cypress/e2e/issue_workflow_test/DPP/credentials.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"credentials": [
{
"type": "digitalProductPassport",
"version": "v0.5.0",
"dataPath": "../../cypress/fixtures/credentials-e2e/DigitalProductPassport_instance-v0.5.0.json",
"url": ""
}
]
}
134 changes: 134 additions & 0 deletions cypress/e2e/issue_workflow_test/DPP/issueDPP.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { LinkType } from "../../../../packages/services/build/linkResolver.service";

describe('Issue DPP end-to-end testing flow', () => {
beforeEach(() => {
// Load app config JSON
cy.fixture('app-config.json').then((data) => {
Cypress.env('AppConfig', data);
});
});

it('should access the right the app config data', () => {
const AppConfig = Cypress.env('AppConfig');
expect(AppConfig?.name).to.eq('CHERRIES SUPPLY CHAIN TRACEABILITY');
});

it('should visit the homepage, navigate to "Orchard Facility", handle API calls, and show success message', () => {
const AppConfig = Cypress.env('AppConfig');
// Visit the homepage
cy.visit('/');

// Verify the "Orchard Facility" link exists and contains the correct text
cy.contains('a', 'Orchard Facility') // Find <a> tag by text
.should('be.visible') // Ensure it's visible
.and('not.be.disabled') // Ensure it's clickable
.click(); // Click the link

// Verify navigation to the correct page
cy.url().should('include', '/orchard-facility');
// Intercept API call triggered by clicking "ISSUE DPP"
const shemaDPP = AppConfig.apps[0]?.features[0]?.components[0].props?.schema?.url;
const appService = AppConfig.apps[0]?.features[0]?.services[0]?.parameters[0];
cy.intercept('GET', shemaDPP)
.as('getProductSchema'); // Create an alias for this request

// Check if "ISSUE DPP" button appears and is interactable
cy.contains('a', 'Issue DPP') // Find button by text
.should('be.visible')
.and('not.be.disabled')
.click();

// Wait for the API call
cy.wait('@getProductSchema', { timeout: 20000 }).then((interception) => {
expect(interception?.response?.statusCode).to.eq(200);
});

const API_ENDPOINT = {
ISSUE_BITSTRING_STATUS_LIST: "/agent/issueBitstringStatusList",
VCKit_URL: appService?.vckit?.vckitAPIUrl + '/credentials/issue',
STORAGE_URL: appService?.storage?.url,
IDR_URL: appService?.dlr?.dlrAPIUrl + appService?.dlr?.linkRegisterPath,
};
cy.intercept('POST', API_ENDPOINT.ISSUE_BITSTRING_STATUS_LIST).as('issueBitStringStatusList');
cy.intercept('POST', API_ENDPOINT.VCKit_URL).as('issueCredentials');
cy.intercept('POST', API_ENDPOINT.STORAGE_URL).as('storeVC');
cy.intercept('POST', API_ENDPOINT.IDR_URL).as('linkResolverRegister');

// Check if "Submit" button appears after API response
cy.contains('button', 'Submit')
.should('be.visible')
.and('not.be.disabled')
.click();

// await API create credential status
cy.wait('@issueBitStringStatusList').then((interception) => {
expect(interception?.response?.statusCode).to.eq(200);
cy.log('Completed: issueBitstringStatusList');
});

// await API issue VC
cy.wait('@issueCredentials').then((interception) => {
const credential = interception.request.body.credential;
cy.log('interception: request: ', JSON.stringify(interception.request.body.credential));
expect(interception?.response?.statusCode).to.eq(201);
// Write the credential to a file
cy.task('writeToFile', { fileName: 'DigitalProductPassport_instance-v0.5.0.json', data: credential });
cy.log('Completed: issueCredentials and written to file');
});

// await API storage VC
cy.wait('@storeVC').then((interception) => {
expect(interception?.response?.statusCode).to.eq(201);
cy.log('Completed: storeVC');

// Extract the URI from the response
const { uri, hash, key } = interception?.response?.body;
expect(uri).to.not.be.undefined;
expect(hash).to.not.be.undefined;
expect(key).to.not.be.undefined;
cy.request('GET', uri).then((response) => {
expect(response.status).to.eq(200);
});
});

// await API register link
cy.wait('@linkResolverRegister').then((interception) => {
expect(interception?.response?.statusCode).to.eq(201);
cy.log('Completed: linkResolverRegister');
});

// Verify toast appears, using react-toastify
cy.get('.Toastify__toast')
.should('be.visible')
.and('contain', 'Action Successful');

// Validate localStorage
cy.window().then((win) => {
const rawData = win.localStorage.getItem('orchard_facility_dpps');
expect(rawData).to.not.be.null;
});
});

it('Verify linkType', () => {
const checkLinkTypeURL = 'http://localhost:3000/gs1/01/09359502000034/10/6789?linkType=gs1:' + LinkType.sustainabilityInfo
cy.request('GET', checkLinkTypeURL).then((response) => {
expect(response.status).to.eq(200);
});
});

it('Runs testing UNTP V0.5.0', () => {
cy.exec('pwd').then((result) => {
cy.log('Current directory:', result.stdout);
});
cy.task('runShellScript', { scriptPath: './cypress/e2e/issue_workflow_test/DPP/test-untp-dpp-scripts.sh' })
.then((output: any) => {
// Loẑi bỏ mã ANSI từ output
const cleanedOutput = output.replace(/\x1b\[[0-9;]*m/g, '');
cy.log('Shell Script Output:', cleanedOutput);
// Expect the output to include success message
expect(cleanedOutput).to.include('Script completed successfully!');
expect(cleanedOutput).to.include('Testing Credential: digitalProductPassport');
expect(cleanedOutput).to.include('Result: PASS');
});
});
});
44 changes: 44 additions & 0 deletions cypress/e2e/issue_workflow_test/DPP/test-untp-dpp-scripts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash

echo "Current working directory: $(pwd)"
# Set variables
SOURCE_FILE="cypress/e2e/issue_workflow_test/DPP/credentials.json"
DEST_FOLDER="packages/untp-test-suite"

# Verify paths
if [ ! -f "$SOURCE_FILE" ]; then
echo "Source file not found: $SOURCE_FILE"
exit 1
fi

if [ ! -d "$DEST_FOLDER" ]; then
echo "Destination folder not found: $DEST_FOLDER"
exit 1
fi

# Copy the JSON file to the new folder
cp "$SOURCE_FILE" "$DEST_FOLDER" || { echo "Failed to copy $SOURCE_FILE to $DEST_FOLDER"; exit 1; }
# Navigate to the new folder
cd "$DEST_FOLDER" || { echo "Failed to navigate to folder: $DEST_FOLDER"; exit 1; }

# Run yarn build
if ! command -v yarn &> /dev/null; then
echo "yarn is not installed. Please install it before running the script."
exit 1
fi

yarn run build || { echo "Build failed"; exit 1; }

# Run npm install globally
if ! command -v npm &> /dev/null; then
echo "npm is not installed. Please install it before running the script."
exit 1
fi

npm install -g . || { echo "Global npm install failed"; exit 1; }

# Run untp script
yarn run untp test || { echo "untp script failed"; exit 1; }

echo "Script completed successfully!"
# End of script
Loading
Loading