Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Fixes for jest transpilation issues. #10788

Merged
merged 17 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e8d2fa2
Fix bug where is-plain-obj is not transpiled by Jest, update lock.
samueljseay Aug 31, 2023
02750f1
Update packages.
samueljseay Sep 1, 2023
acbb01d
Fix regex pattern for transforming some node_modules.
samueljseay Sep 1, 2023
62f1eb0
Mock useSelect for a handful of RichText selectors in test.
samueljseay Sep 1, 2023
4a892e7
Resolve react to single version to avoid invalid hook errors.
samueljseay Sep 1, 2023
80b00ef
Patch trim-html locally to avoid a bug in the released npm source.
samueljseay Sep 1, 2023
e479c88
Mock out resizeObserver to avoid https://github.com/FezVrasta/react-r…
samueljseay Sep 1, 2023
c433455
Merge remote-tracking branch 'origin/trunk' into dev/fix-10523
samueljseay Sep 1, 2023
5287e3b
Remove commented out code.
samueljseay Sep 1, 2023
b1342e2
Replace space with tabs to match file indentation.
samueljseay Sep 1, 2023
f3ac98c
Update e2e config's transformIgnorePatterns for memize and other poss…
samueljseay Sep 2, 2023
93015e3
Merge remote-tracking branch 'origin/trunk' into dev/fix-10523
samueljseay Sep 2, 2023
88e2e48
Ignore config package: https://github.com/node-config/node-config/iss…
samueljseay Sep 4, 2023
15255f8
Merge branch 'trunk' into dev/fix-10523
samueljseay Sep 4, 2023
881cb4d
Add a comment explaining that trim-html is copy/pasted.
samueljseay Sep 4, 2023
1c6772b
Merge remote-tracking branch 'origin/trunk' into dev/fix-10523
samueljseay Sep 4, 2023
32161ca
Merge remote-tracking branch 'origin/trunk' into dev/fix-10523
samueljseay Sep 5, 2023
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
155 changes: 155 additions & 0 deletions assets/js/base/components/read-more/trim-html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
export function trimHtml( html, options ) {
Copy link
Contributor Author

@samueljseay samueljseay Sep 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is a bit crazy. I'm not sure how we never ran into this, maybe because technically its ok not to declare a variable before access but I thought browsers having use strict would make this throw.

So the issue is that in the published version of trim-html there is no declaration of the variable rowCut which causes tests to throw an exception. You can verify the declaration is missing by downloading the npm module and checking the source.

This uses the repository source which has the problem fixed. I did consider implementing something myself with Domparser but decided against it because there is nuanced behaviour of the truncation. Thankfully we have tests covering regressions here.

We could also use a tool like patch package if its preferred. https://www.npmjs.com/package/patch-package

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a comment that explains that this code is copy-pasted.

options = options || {};

const limit = options.limit || 100,
preserveTags =
typeof options.preserveTags !== 'undefined'
? options.preserveTags
: true,
wordBreak =
typeof options.wordBreak !== 'undefined'
? options.wordBreak
: false,
suffix = options.suffix || '...',
moreLink = options.moreLink || '',
moreText = options.moreText || '»',
preserveWhiteSpace = options.preserveWhiteSpace || false;

const arr = html
.replace( /</g, '\n<' )
.replace( />/g, '>\n' )
.replace( /\n\n/g, '\n' )
.replace( /^\n/g, '' )
.replace( /\n$/g, '' )
.split( '\n' );

let sum = 0,
row,
cut,
add,
rowCut,
tagMatch,
tagName,
// eslint-disable-next-line prefer-const
tagStack = [],
more = false;

for ( let i = 0; i < arr.length; i++ ) {
row = arr[ i ];

// count multiple spaces as one character
if ( ! preserveWhiteSpace ) {
rowCut = row.replace( /[ ]+/g, ' ' );
} else {
rowCut = row;
}

if ( ! row.length ) {
continue;
}

const charArr = getCharArr( rowCut );

if ( row[ 0 ] !== '<' ) {
if ( sum >= limit ) {
row = '';
} else if ( sum + charArr.length >= limit ) {
cut = limit - sum;

if ( charArr[ cut - 1 ] === ' ' ) {
while ( cut ) {
cut -= 1;
if ( charArr[ cut - 1 ] !== ' ' ) {
break;
}
}
} else {
add = charArr.slice( cut ).indexOf( ' ' );

// break on halh of word
if ( ! wordBreak ) {
if ( add !== -1 ) {
cut += add;
} else {
cut = row.length;
}
}
}

row = charArr.slice( 0, cut ).join( '' ) + suffix;

if ( moreLink ) {
row +=
'<a href="' +
moreLink +
'" style="display:inline">' +
moreText +
'</a>';
}

sum = limit;
more = true;
} else {
sum += charArr.length;
}
} else if ( ! preserveTags ) {
row = '';
} else if ( sum >= limit ) {
tagMatch = row.match( /[a-zA-Z]+/ );
tagName = tagMatch ? tagMatch[ 0 ] : '';

if ( tagName ) {
if ( row.substring( 0, 2 ) !== '</' ) {
tagStack.push( tagName );
row = '';
} else {
while (
tagStack[ tagStack.length - 1 ] !== tagName &&
tagStack.length
) {
tagStack.pop();
}

if ( tagStack.length ) {
row = '';
}

tagStack.pop();
}
} else {
row = '';
}
}

arr[ i ] = row;
}

return {
html: arr.join( '\n' ).replace( /\n/g, '' ),
more,
};
}

// count symbols like one char
function getCharArr( rowCut ) {
// eslint-disable-next-line prefer-const
let charArr = [],
subRow,
match,
char;

for ( let i = 0; i < rowCut.length; i++ ) {
subRow = rowCut.substring( i );
match = subRow.match( /^&[a-z0-9#]+;/ );

if ( match ) {
char = match[ 0 ];
charArr.push( char );
i += char.length - 1;
} else {
charArr.push( rowCut[ i ] );
}
}

return charArr;
}
7 changes: 5 additions & 2 deletions assets/js/base/components/read-more/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/**
* External dependencies
* Internal dependencies
*/
import trimHtml from 'trim-html';
import { trimHtml } from './trim-html';

/**
* External dependencies
*/
type Markers = {
end: number;
middle: number;
Expand Down
5 changes: 5 additions & 0 deletions assets/js/blocks/cart/test/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ import OrderSummarySubtotalBlock from '../inner-blocks/cart-order-summary-subtot
import OrderSummaryShippingBlock from '../inner-blocks/cart-order-summary-shipping/frontend';
import OrderSummaryTaxesBlock from '../inner-blocks/cart-order-summary-taxes/frontend';

jest.mock( '@wordpress/compose', () => ( {
...jest.requireActual( '@wordpress/compose' ),
useResizeObserver: jest.fn().mockReturnValue( [ null, { width: 0 } ] ),
Copy link
Contributor Author

@samueljseay samueljseay Sep 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in my commit this is something I have actually run into before, if you're including useResizeObserver as a dependency outside of WP (such as in a jest test) then you get an error because the underlying library (react-resize-aware) cannot access the jsx runtime due to how it's built. See FezVrasta/react-resize-aware#58

If we had pnpm we could force resolution to a newer version, but in npm the solutions to this are hackier so we just stub it out here instead.

} ) );

const CartBlock = ( {
attributes = {
showRateAfterTaxName: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,31 @@ import { render, queryByText } from '@testing-library/react';
* Internal dependencies
*/
import { Edit } from '../edit';
const blockSettingsMock = jest.requireMock( '@woocommerce/block-settings' );

jest.mock( '@wordpress/data', () => ( {
...jest.requireActual( '@wordpress/data' ),
useSelect: jest.fn().mockImplementation( ( fn ) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bit tricky, I had to mock a few useSelect cases to avoid exceptions in underlying RichText component.

const select = () => {
return {
getSelectionStart: () => ( {
clientId: null,
} ),
getSelectionEnd: () => ( {
clientId: null,
} ),
getFormatTypes: () => [],
};
};

if ( typeof fn === 'function' ) {
return fn( select );
}

return {
isCaretWithinFormattedText: () => false,
};
} ),
} ) );

jest.mock( '@wordpress/block-editor', () => ( {
...jest.requireActual( '@wordpress/block-editor' ),
Expand All @@ -21,6 +45,8 @@ jest.mock( '@woocommerce/block-settings', () => ( {
TERMS_URL: '/terms-and-conditions',
} ) );

const blockSettingsMock = jest.requireMock( '@woocommerce/block-settings' );

describe( 'Edit', () => {
it( 'Renders a checkbox if the checkbox attribute is true', async () => {
const { container } = render(
Expand Down
Loading