Skip to content
Merged
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
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# @desktop @mobile @enabled
language: node_js
dist: 'focal'
branches:
only:
- master
node_js:
- '22'
addons:
Expand Down Expand Up @@ -28,7 +31,7 @@ jobs:
script: yarn test
- name: 'E2E Tests'
if: fork = false # Note: We can only run E2E tests on canonical due to security concerns
script: travis_wait 60 yarn test:e2e
script: yarn test:e2e
notifications:
email:
recipients:
Expand Down
17 changes: 17 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const { defineConfig } = require('cypress');

module.exports = defineConfig({
defaultCommandTimeout: 8000,
fileServerFolder: 'test',
fixturesFolder: 'test/fixtures',
screenshotsFolder: 'test/screenshots',
video: false,
videosFolder: 'test/videos',
viewportHeight: 1260,
viewportWidth: 1600,
e2e: {
baseUrl: 'http://localhost:8001/#',
specPattern: 'test/integration/**/*.test.{js,jsx,ts,tsx}',
supportFile: 'test/support/index.js',
},
});
14 changes: 0 additions & 14 deletions cypress.json

This file was deleted.

40 changes: 21 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
"description": "Box Annotations",
"author": "Box (https://www.box.com/)",
"license": "SEE LICENSE IN LICENSE",
"sideEffects": [
"./dist/annotations.js",
"**/*.css",
"**/*.scss"
],
"sideEffects": ["./dist/annotations.js", "**/*.css", "**/*.scss"],
"repository": {
"type": "git",
"url": "git@github.com:box/box-annotations.git"
Expand All @@ -17,7 +13,7 @@
"registry": "https://registry.npmjs.org/"
},
"engines": {
"node": ">=18.x",
"node": ">=20.x",
"yarn": ">=1.10.x"
},
"devDependencies": {
Expand All @@ -32,14 +28,17 @@
"@babel/preset-env": "^7.25.9",
"@babel/preset-react": "^7.25.9",
"@babel/preset-typescript": "^7.25.9",
"@box/blueprint-web": "^16.12.0",
"@box/blueprint-web-assets": "^5.5.12",
"@box/collaboration-popover": "^2.1.32",
"@box/combobox-with-api": "^1.42.22",
"@box/frontend": "^10.0.0",
"@box/blueprint-web": "^16.8.2",
"@box/blueprint-web-assets": "^5.5.0",
"@box/collaboration-popover": "^2.1.25",
"@box/item-icon": "^3.2.0",
"@box/languages": "^1.1.0",
"@box/readable-time": "^2.1.25",
"@box/readable-time": "^2.1.32",
"@box/threaded-annotations": "^4.0.6",
"@box/user-selector": "^2.1.27",
"@box/types": "^2.1.8",
"@box/user-selector": "^2.1.35",
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.2.0",
Expand Down Expand Up @@ -78,7 +77,7 @@
"core-js": "^3.38.1",
"css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.4",
"cypress": "^4.4.1",
"cypress": "^15.20.1",
"draft-js": "0.10.5",
"enzyme": "^3.11.0",
"enzyme-to-json": "^3.4.4",
Expand Down Expand Up @@ -126,19 +125,22 @@
"tabbable": "^1.1.3",
"typescript": "4.9.5",
"uuid": "^8.3.1",
"wait-on": "^4.0.2",
"wait-on": "^9.0.5",
"webpack": "^5.105.4",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.2.3",
"worker-farm": "^1.7.0"
},
"peerDependencies": {
"@box/blueprint-web": "^16.8.2",
"@box/blueprint-web-assets": "^5.5.0",
"@box/collaboration-popover": "^2.1.25",
"@box/readable-time": "^2.1.25",
"@box/blueprint-web": "^16.12.0",
"@box/blueprint-web-assets": "^5.5.12",
"@box/collaboration-popover": "^2.1.32",
"@box/combobox-with-api": "^1.42.22",
"@box/item-icon": "^3.2.0",
"@box/readable-time": "^2.1.32",
"@box/threaded-annotations": "^4.0.6",
"@box/user-selector": "^2.1.27"
"@box/types": "^2.1.8",
"@box/user-selector": "^2.1.35"
},
"scripts": {
"build": "yarn setup && yarn build:prod:dist",
Expand All @@ -148,7 +150,7 @@
"clean": "rm -rf dist reports/* i18n/json i18n/*.js",
"cy:open": "yarn cy:wait; yarn cypress open",
"cy:run": "yarn cy:wait; yarn cypress run --spec test/integration/**/*.test.js",
"cy:wait": "wait-on http-get://localhost:8001",
"cy:wait": "wait-on http-get://127.0.0.1:8001",
"lint": "npm-run-all lint:*",
"lint:js": "eslint .",
"lint:scss": "stylelint \"src/**/*.scss\" --syntax scss",
Expand Down
21 changes: 10 additions & 11 deletions scripts/cypress.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,26 @@ async function main() {
console.log('Cypress run starting...');

const suffix = argv.indexOf('-o') >= 0 ? 'open' : 'run'; // Pass -o to run Cypress in "open" mode
const output = childProcess.execSync(`yarn npm-run-all -p -r start:dev cy:${suffix}`, {
const result = childProcess.spawnSync('yarn', ['npm-run-all', '-p', '-r', 'start:dev', `cy:${suffix}`], {
env: {
...env,
CYPRESS_ACCESS_TOKEN: E2E_ACCESS_TOKEN,
CYPRESS_FILE_ID_DOC: documentId,
CYPRESS_FILE_ID_IMAGE: imageId,
},
stdio: 'inherit',
});

console.log('Cypress run SUCCESS. Output:');
console.log('------------------------------');
console.log(output.toString());
} catch (error) {
console.log('Cypress run FAILURE. Output:');
console.log('------------------------------');
console.log(error.stdout.toString());
process.exitCode = error && error.status ? error.status : 0;
if (result.status === 0) {
console.log('Cypress run SUCCESS.');
} else {
console.log('Cypress run FAILURE.');
process.exitCode = result.status || 1;
}
} finally {
await cleanup(folderId);
}

await cleanup(folderId);

console.log('Test script complete. Exiting.');
}

Expand Down
51 changes: 31 additions & 20 deletions scripts/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const moduleExternals = [
const matchesPackage = (request, name) => request === name || request.startsWith(`${name}/`);

const isDev = process.env.NODE_ENV === 'dev';
const isDevServer = Boolean(process.env.WEBPACK_SERVE);
const isLinked = process.env.IS_LINKED === '1';
const isRelease = process.env.NODE_ENV === 'production';
const language = process.env.LANGUAGE || 'en-US';
Expand All @@ -50,29 +51,39 @@ const config = Object.assign(commonConfig(), {
entry: {
annotations: ['./src/BoxAnnotations.ts'],
},
externals: [
({ request }, callback) => {
if (!request) {
return callback();
}
if (commonjsExternals.some(name => matchesPackage(request, name))) {
return callback(null, `commonjs ${request}`);
}
if (moduleExternals.some(name => matchesPackage(request, name))) {
return callback(null, `module ${request}`);
}
return callback();
},
],
experiments: {
outputModule: true,
},
// The test page loads annotations.js with a classic script tag, so the
// webpack-dev-server build inlines deps and emits a non-module file.
...(isDevServer
? {}
: {
externals: [
({ request }, callback) => {
if (!request) {
return callback();
}
if (commonjsExternals.some(name => matchesPackage(request, name))) {
return callback(null, `commonjs ${request}`);
}
if (moduleExternals.some(name => matchesPackage(request, name))) {
return callback(null, `module ${request}`);
}
return callback();
},
],
experiments: {
outputModule: true,
},
}),
output: {
filename: '[name].js',
library: {
type: 'module',
},
path: path.resolve('dist'),
...(isDevServer
? {}
: {
library: {
type: 'module',
},
}),
},
resolve: {
alias: {
Expand Down
4 changes: 2 additions & 2 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
<meta name="viewport" content="width=device-width" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link rel="stylesheet" href="./styles.css" />
<link rel="stylesheet" href="https://cdn01.boxcdn.net/platform/preview/2.64.0/en-US/preview.css" />
<link rel="stylesheet" href="https://cdn01.boxcdn.net/platform/preview/3.79.0/en-US/preview.css" />
<link rel="stylesheet" href="./annotations.css" />
<script src="https://cdn01.boxcdn.net/polyfills/core-js/2.5.3/core.min.js"></script>
<script src="https://cdn01.boxcdn.net/platform/preview/2.64.0/en-US/preview.js"></script>
<script src="https://cdn01.boxcdn.net/platform/preview/3.79.0/en-US/preview.js"></script>
<script src="./annotations.js"></script>
</head>

Expand Down
43 changes: 15 additions & 28 deletions test/integration/Drawing.e2e.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ describe('Drawing', () => {
// Assert that at least one annotation is present on the document and is active
cy.get('.ba-DrawingTarget').should('have.class', 'is-active');

// Exit drawing creation mode
cy.getByTestId('bp-AnnotationsControls-drawBtn').click();
// Exit drawing creation mode; DrawingCreator overlays the toolbar button
cy.getByTestId('bp-AnnotationsControls-drawBtn').click({ force: true });

// Assert that annotation target is not active
cy.get('.ba-DrawingTarget').should('not.have.class', 'is-active');
Expand Down Expand Up @@ -104,8 +104,8 @@ describe('Drawing', () => {
// Assert that at least one annotation is present on the image and is active
cy.get('.ba-DrawingTarget').should('have.class', 'is-active');

// Exit drawing creation mode
cy.getByTestId('bp-AnnotationsControls-drawBtn').click();
// Exit drawing creation mode; DrawingCreator overlays the toolbar button
cy.getByTestId('bp-AnnotationsControls-drawBtn').click({ force: true });

// Select annotation target
cy.get('.ba-DrawingTarget').click();
Expand All @@ -120,40 +120,27 @@ describe('Drawing', () => {
cy.get('.ba-DrawingTarget').should('have.class', 'is-active');
});

it('should hide drawing button for rotated image', () => {
// Show the preview
it('should preserve drawing annotations across image rotation', () => {
cy.showPreview(Cypress.env('FILE_ID_IMAGE'));

// Assert drawing button is not hidden
cy.getByTestId('bp-AnnotationsControls-drawBtn')
.should('be.visible')
.click();
cy.getByTestId('bp-AnnotationsControls-drawBtn').click({ force: true });

// Add a drawing annotation on the image
cy.drawStroke();
cy.getByTestId('ba-PopupDrawingToolbar-comment').click();
cy.submitReply();

// Assert that at least one annotation is present on the image
cy.get('.ba-DrawingTarget').should('be.visible');
cy.get('.ba-DrawingTarget').should('exist');

// Rotate image
cy.getByTitle('Rotate left').click();
cy.getByTitle('Rotate left').click({ force: true });
cy.get('.ba-DrawingTarget').should('exist');

// Assert drawing button is hidden
cy.getByTestId('bp-AnnotationsControls-drawBtn').should('not.be.visible');
// Assert that drawing annotations are still visible after rotation
cy.get('.ba-DrawingTarget').should('be.visible');
cy.getByTitle('Rotate left').click({ force: true });
cy.get('.ba-DrawingTarget').should('exist');

// Rotate image back to non-rotated state
cy.getByTitle('Rotate left')
.click()
.click()
.click();
cy.getByTitle('Rotate left').click({ force: true });
cy.get('.ba-DrawingTarget').should('exist');

// Assert drawing button is not hidden
cy.getByTestId('bp-AnnotationsControls-drawBtn').should('be.visible');
// Assert that drawing annotations are still visible after rotation
cy.get('.ba-DrawingTarget').should('be.visible');
cy.getByTitle('Rotate left').click({ force: true });
cy.get('.ba-DrawingTarget').should('exist');
});
});
19 changes: 6 additions & 13 deletions test/integration/Highlight.e2e.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,18 @@ describe('Highlights', () => {
cy.selectText();
cy.submitReply();

// Assert that at least one highlight annotation is present on the document and is active
// Newly-created highlight is active
cy.get('.ba-HighlightTarget').should('have.class', 'is-active');

// Exit highlight creation mode
// Exit highlight creation mode — the just-created target remains active
cy.getByTestId('bp-AnnotationsControls-highlightBtn').click();
cy.get('.ba-HighlightTarget').should('have.class', 'is-active');

// Assert that annotation target is not active
cy.get('.ba-HighlightTarget').should('not.have.class', 'is-active');

// Select annotation target
// Clicking the already-active target is a noop
cy.get('.ba-HighlightTarget-rect').click();

// Assert that annotation target is active
cy.get('.ba-HighlightTarget').should('have.class', 'is-active');

// Select annotation target again should be a noop, it should remain active
cy.get('.ba-HighlightTarget-rect').click();

// Assert that annotation target is active
cy.get('.ba-HighlightTarget').should('have.class', 'is-active');

// Select text to trigger promotion flow
Expand Down Expand Up @@ -69,14 +62,14 @@ describe('Highlights', () => {
// Alias the last text block of the first textLayer
cy.get('[data-page-number="1"')
.find('.textLayer')
.children()
.children('span')
.last()
.as('pageOneEndTextEl');

// Select texts across pages
cy.get('[data-page-number="2"')
.find('.textLayer')
.children()
.children('span')
.first()
.then($pageTwoStartTextEl => {
cy.get('@pageOneEndTextEl')
Expand Down
Loading