Commit ae24a16652c for woocommerce
commit ae24a16652c9bc9813e3fa00de49cf9f0fe2c6ab
Author: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
Date: Fri Jun 26 17:49:28 2026 +0200
Turn Blocks non-null assertion linting from warn to errors (and fix existing instances) (#66010)
* Harden Blocks non-null assertion linting
* Add changelog for Blocks non-null assertion linting
diff --git a/plugins/woocommerce/changelog/codex-fix-65992-non-null-assertions b/plugins/woocommerce/changelog/codex-fix-65992-non-null-assertions
new file mode 100644
index 00000000000..0cce588f1f5
--- /dev/null
+++ b/plugins/woocommerce/changelog/codex-fix-65992-non-null-assertions
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Harden Blocks non-null assertion lint rule to error.
diff --git a/plugins/woocommerce/client/blocks/.eslintrc.js b/plugins/woocommerce/client/blocks/.eslintrc.js
index e80d9420711..40b04a566dc 100644
--- a/plugins/woocommerce/client/blocks/.eslintrc.js
+++ b/plugins/woocommerce/client/blocks/.eslintrc.js
@@ -258,6 +258,7 @@ module.exports = {
plugins: [ 'jest', '@typescript-eslint' ],
extends: [ 'plugin:jest/recommended' ],
rules: {
+ '@typescript-eslint/no-non-null-assertion': 'error',
'jest/no-mocks-import': 'off',
// With React Testing library, it is expected use expect() in the waitFor() function: https://testing-library.com/docs/dom-testing-library/api-async/
'jest/no-standalone-expect': 'off',
@@ -278,6 +279,7 @@ module.exports = {
],
rules: {
'@typescript-eslint/no-explicit-any': 'error',
+ '@typescript-eslint/no-non-null-assertion': 'error',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': [ 'error' ],
'jsdoc/require-param': 'off',
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-schema-parser.tsx b/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-schema-parser.tsx
index 0d471e3d270..68b60eb5df0 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-schema-parser.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/hooks/test/use-schema-parser.tsx
@@ -10,7 +10,7 @@ import { WPDataRegistry } from '@wordpress/data/build-types/registry';
/**
* Internal dependencies
*/
-import { useSchemaParser } from '../use-schema-parser';
+import { useSchemaParser, type DocumentObject } from '../use-schema-parser';
import { CheckoutState } from '../../../data/checkout/default-state';
import { PaymentState } from '../../../data/payment/default-state';
import { CartState } from '../../../data/cart/default-state';
@@ -36,6 +36,16 @@ type DeepPartial< T > = T extends object
}
: T;
+const getCurrentData = < T extends FormType | 'global' >( result: {
+ current: { data: DocumentObject< T > | null };
+} ): DocumentObject< T > => {
+ const { data } = result.current;
+ if ( data === null ) {
+ throw new Error( 'Expected schema parser data to be available.' );
+ }
+ return data;
+};
+
describe( 'useSchemaParser', () => {
let registry: WPDataRegistry;
let mockCartData: DeepPartial< CartState[ 'cartData' ] >;
@@ -242,7 +252,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { cart } = result.current.data!;
+ const { cart } = getCurrentData( result );
expect( cart ).toHaveProperty( 'coupons' );
expect( cart.coupons ).toEqual( [ 'SAVE10', 'FREESHIP' ] );
@@ -291,7 +301,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { checkout } = result.current.data!;
+ const { checkout } = getCurrentData( result );
expect( checkout ).toHaveProperty( 'create_account' );
expect( checkout.create_account ).toBe( true );
@@ -319,7 +329,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { customer } = result.current.data!;
+ const { customer } = getCurrentData( result );
expect( customer ).toHaveProperty( 'id' );
expect( customer.id ).toBe( 123 );
@@ -355,7 +365,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { customer } = result.current.data!;
+ const { customer } = getCurrentData( result );
expect( customer.address ).toEqual( mockCartData.billingAddress );
} );
@@ -368,7 +378,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { customer } = result.current.data!;
+ const { customer } = getCurrentData( result );
expect( customer.address ).toEqual( mockCartData.shippingAddress );
} );
@@ -381,7 +391,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { customer } = result.current.data!;
+ const { customer } = getCurrentData( result );
expect( customer.address ).toBeUndefined();
} );
@@ -394,7 +404,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { customer } = result.current.data!;
+ const { customer } = getCurrentData( result );
expect( customer.address ).toBeUndefined();
} );
@@ -407,7 +417,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { customer } = result.current.data!;
+ const { customer } = getCurrentData( result );
expect( customer.address ).toBeUndefined();
} );
} );
@@ -422,7 +432,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { customer } = result.current.data!;
+ const { customer } = getCurrentData( result );
expect( customer.additional_fields ).toEqual( {
'namespace/contact_field': 'value1',
} );
@@ -437,7 +447,7 @@ describe( 'useSchemaParser', () => {
}
);
- const { checkout } = result.current.data!;
+ const { checkout } = getCurrentData( result );
expect( checkout.additional_fields ).toEqual( {
'namespace/order_field': 'value2',
} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/mutation-batcher.test.ts b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/mutation-batcher.test.ts
index 6a393a28367..ab48d9aae12 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/mutation-batcher.test.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/mutation-batcher.test.ts
@@ -35,6 +35,17 @@ function flushMicrotasks() {
return new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
}
+function resolvePendingFetch(
+ fetchPromise: { resolve?: ( value: Response ) => void },
+ response: Response
+) {
+ const { resolve } = fetchPromise;
+ if ( ! resolve ) {
+ throw new Error( 'Expected fetch resolver to be set.' );
+ }
+ resolve( response );
+}
+
describe( 'createMutationQueue', () => {
let originalFetch: typeof global.fetch;
let mockState: TestState;
@@ -846,8 +857,8 @@ describe( 'createMutationQueue', () => {
...stateHandler,
} );
- // Should resolve immediately — nothing in progress.
- await queue.waitForIdle();
+ // Should resolve immediately: nothing in progress.
+ await expect( queue.waitForIdle() ).resolves.toBeUndefined();
} );
it( 'resolves after the processing cycle completes', async () => {
@@ -877,7 +888,7 @@ describe( 'createMutationQueue', () => {
expect( idleResolved ).toBe( false );
// Resolve the fetch — completes the cycle.
- fetchPromise.resolve!( {
+ resolvePendingFetch( fetchPromise, {
ok: true,
json: () =>
Promise.resolve( {
@@ -923,7 +934,7 @@ describe( 'createMutationQueue', () => {
expect( waiter1Resolved ).toBe( false );
expect( waiter2Resolved ).toBe( false );
- fetchPromise.resolve!( {
+ resolvePendingFetch( fetchPromise, {
ok: true,
json: () =>
Promise.resolve( {
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/products.test.ts b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/products.test.ts
index 646e66db543..d2629ec5d6f 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/products.test.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/test/products.test.ts
@@ -12,9 +12,20 @@ let mockRegisteredStore: {
state: ProductsStore[ 'state' ];
} | null = null;
+let mockStoreState: ProductsStore[ 'state' ];
+
let mockContext: { productId?: number; variationId?: number | null } | null =
null;
+const getMockStoreState = (): ProductsStore[ 'state' ] => {
+ if ( mockRegisteredStore === null ) {
+ throw new Error(
+ 'Expected woocommerce/products store to be registered.'
+ );
+ }
+ return mockRegisteredStore.state;
+};
+
const mockProduct = {
id: 42,
name: 'Test Product',
@@ -66,132 +77,95 @@ describe( 'woocommerce/products store – product context derived state', () =>
mockContext = null;
jest.isolateModules( () => require( '../products' ) );
+ mockStoreState = getMockStoreState();
// Hydrate products and variations after store is created.
- mockRegisteredStore!.state.products = { 42: mockProduct };
- mockRegisteredStore!.state.productVariations = { 99: mockVariation };
+ mockStoreState.products = { 42: mockProduct };
+ mockStoreState.productVariations = { 99: mockVariation };
} );
it( 'has writable productId and variationId state', () => {
- expect( mockRegisteredStore ).not.toBeNull();
+ mockStoreState.productId = 42;
+ mockStoreState.variationId = 99;
- mockRegisteredStore!.state.productId = 42;
- mockRegisteredStore!.state.variationId = 99;
-
- expect( mockRegisteredStore!.state.productId ).toBe( 42 );
- expect( mockRegisteredStore!.state.variationId ).toBe( 99 );
+ expect( mockStoreState.productId ).toBe( 42 );
+ expect( mockStoreState.variationId ).toBe( 99 );
} );
describe( 'mainProductInContext', () => {
it( 'returns the product when variationId is null', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- mockRegisteredStore!.state.productId = 42;
- mockRegisteredStore!.state.variationId = null;
+ mockStoreState.productId = 42;
+ mockStoreState.variationId = null;
- expect( mockRegisteredStore!.state.mainProductInContext ).toBe(
- mockProduct
- );
+ expect( mockStoreState.mainProductInContext ).toBe( mockProduct );
} );
it( 'returns the product even when variationId is set', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- mockRegisteredStore!.state.productId = 42;
- mockRegisteredStore!.state.variationId = 99;
+ mockStoreState.productId = 42;
+ mockStoreState.variationId = 99;
// product always returns the main product, never the variation.
- expect( mockRegisteredStore!.state.mainProductInContext ).toBe(
- mockProduct
- );
+ expect( mockStoreState.mainProductInContext ).toBe( mockProduct );
} );
it( 'returns null when product is not in the store', () => {
- expect( mockRegisteredStore ).not.toBeNull();
+ mockStoreState.productId = 999;
+ mockStoreState.variationId = null;
- mockRegisteredStore!.state.productId = 999;
- mockRegisteredStore!.state.variationId = null;
-
- expect(
- mockRegisteredStore!.state.mainProductInContext
- ).toBeNull();
+ expect( mockStoreState.mainProductInContext ).toBeNull();
} );
it( 'returns null when productId is 0', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- expect(
- mockRegisteredStore!.state.mainProductInContext
- ).toBeNull();
+ expect( mockStoreState.mainProductInContext ).toBeNull();
} );
it( 'reads from block context when available', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- mockRegisteredStore!.state.productId = 1;
+ mockStoreState.productId = 1;
mockContext = { productId: 42 };
- expect( mockRegisteredStore!.state.mainProductInContext ).toBe(
- mockProduct
- );
+ expect( mockStoreState.mainProductInContext ).toBe( mockProduct );
} );
} );
describe( 'productVariationInContext', () => {
it( 'returns null when variationId is null (simple product)', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- mockRegisteredStore!.state.productId = 42;
- mockRegisteredStore!.state.variationId = null;
+ mockStoreState.productId = 42;
+ mockStoreState.variationId = null;
- expect(
- mockRegisteredStore!.state.productVariationInContext
- ).toBeNull();
+ expect( mockStoreState.productVariationInContext ).toBeNull();
} );
it( 'returns null when variationId is null (variable product, no selection)', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- mockRegisteredStore!.state.products[ 10 ] = {
+ mockStoreState.products[ 10 ] = {
id: 10,
type: 'variable',
} as ProductResponseItem;
- mockRegisteredStore!.state.productId = 10;
- mockRegisteredStore!.state.variationId = null;
+ mockStoreState.productId = 10;
+ mockStoreState.variationId = null;
- expect(
- mockRegisteredStore!.state.productVariationInContext
- ).toBeNull();
+ expect( mockStoreState.productVariationInContext ).toBeNull();
} );
it( 'returns the variation when variationId is set', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- mockRegisteredStore!.state.productId = 42;
- mockRegisteredStore!.state.variationId = 99;
+ mockStoreState.productId = 42;
+ mockStoreState.variationId = 99;
- expect( mockRegisteredStore!.state.productVariationInContext ).toBe(
+ expect( mockStoreState.productVariationInContext ).toBe(
mockVariation
);
} );
it( 'returns null when variation is not in the store', () => {
- expect( mockRegisteredStore ).not.toBeNull();
+ mockStoreState.productId = 42;
+ mockStoreState.variationId = 999;
- mockRegisteredStore!.state.productId = 42;
- mockRegisteredStore!.state.variationId = 999;
-
- expect(
- mockRegisteredStore!.state.productVariationInContext
- ).toBeNull();
+ expect( mockStoreState.productVariationInContext ).toBeNull();
} );
} );
describe( 'findProduct', () => {
it( 'returns null when product is not in the store', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 999,
} );
@@ -199,15 +173,13 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'returns the product itself for a simple product', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const simpleProduct = {
id: 1,
type: 'simple',
} as ProductResponseItem;
- mockRegisteredStore!.state.products[ 1 ] = simpleProduct;
+ mockStoreState.products[ 1 ] = simpleProduct;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 1,
} );
@@ -215,8 +187,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'returns the matched variation when selectedAttributes match and the variation is populated', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variableProduct = {
id: 1,
type: 'variable',
@@ -231,11 +201,10 @@ describe( 'woocommerce/products store – product context derived state', () =>
id: 10,
name: 'Red Variation',
} as ProductResponseItem;
- mockRegisteredStore!.state.products[ 1 ] = variableProduct;
- mockRegisteredStore!.state.productVariations[ 10 ] =
- populatedVariation;
+ mockStoreState.products[ 1 ] = variableProduct;
+ mockStoreState.productVariations[ 10 ] = populatedVariation;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 1,
selectedAttributes: [ { attribute: 'Color', value: 'red' } ],
} );
@@ -244,8 +213,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'returns null when attributes match but the variation is not populated', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variableProduct = {
id: 1,
type: 'variable',
@@ -256,10 +223,10 @@ describe( 'woocommerce/products store – product context derived state', () =>
},
],
} as unknown as ProductResponseItem;
- mockRegisteredStore!.state.products[ 1 ] = variableProduct;
+ mockStoreState.products[ 1 ] = variableProduct;
// productVariations intentionally empty.
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 1,
selectedAttributes: [ { attribute: 'Color', value: 'red' } ],
} );
@@ -268,8 +235,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'returns the parent product when the product is variable and no attributes are selected', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variableProduct = {
id: 1,
type: 'variable',
@@ -280,13 +245,13 @@ describe( 'woocommerce/products store – product context derived state', () =>
},
],
} as unknown as ProductResponseItem;
- mockRegisteredStore!.state.products[ 1 ] = variableProduct;
+ mockStoreState.products[ 1 ] = variableProduct;
- expect( mockRegisteredStore!.state.findProduct( { id: 1 } ) ).toBe(
+ expect( mockStoreState.findProduct( { id: 1 } ) ).toBe(
variableProduct
);
expect(
- mockRegisteredStore!.state.findProduct( {
+ mockStoreState.findProduct( {
id: 1,
selectedAttributes: [],
} )
@@ -294,8 +259,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'returns null when attributes do not match any variation', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variableProduct = {
id: 1,
type: 'variable',
@@ -306,12 +269,12 @@ describe( 'woocommerce/products store – product context derived state', () =>
},
],
} as unknown as ProductResponseItem;
- mockRegisteredStore!.state.products[ 1 ] = variableProduct;
- mockRegisteredStore!.state.productVariations[ 10 ] = {
+ mockStoreState.products[ 1 ] = variableProduct;
+ mockStoreState.productVariations[ 10 ] = {
id: 10,
} as ProductResponseItem;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 1,
selectedAttributes: [ { attribute: 'Color', value: 'blue' } ],
} );
@@ -320,15 +283,13 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'returns the variation directly when given a variation ID', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variation = {
id: 50,
name: 'Direct Variation',
} as ProductResponseItem;
- mockRegisteredStore!.state.productVariations[ 50 ] = variation;
+ mockStoreState.productVariations[ 50 ] = variation;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 50,
} );
@@ -336,15 +297,13 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'returns the variation directly and ignores selectedAttributes when given a variation ID', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variation = {
id: 50,
name: 'Direct Variation',
} as ProductResponseItem;
- mockRegisteredStore!.state.productVariations[ 50 ] = variation;
+ mockStoreState.productVariations[ 50 ] = variation;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 50,
selectedAttributes: [ { attribute: 'Color', value: 'blue' } ],
} );
@@ -353,8 +312,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'prefers variation lookup over product lookup when ID exists in both', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const product = {
id: 50,
type: 'simple',
@@ -364,10 +321,10 @@ describe( 'woocommerce/products store – product context derived state', () =>
id: 50,
name: 'Variation 50',
} as ProductResponseItem;
- mockRegisteredStore!.state.products[ 50 ] = product;
- mockRegisteredStore!.state.productVariations[ 50 ] = variation;
+ mockStoreState.products[ 50 ] = product;
+ mockStoreState.productVariations[ 50 ] = variation;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 50,
} );
@@ -376,8 +333,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
describe( 'attribute matching (variable products)', () => {
it( 'matches with attribute prefix in selected attributes', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variableProduct = {
id: 3,
type: 'variable',
@@ -406,13 +361,11 @@ describe( 'woocommerce/products store – product context derived state', () =>
id: 302,
name: 'Blue Large',
} as ProductResponseItem;
- mockRegisteredStore!.state.products[ 3 ] = variableProduct;
- mockRegisteredStore!.state.productVariations[ 301 ] =
- populatedVariation301;
- mockRegisteredStore!.state.productVariations[ 302 ] =
- populatedVariation302;
+ mockStoreState.products[ 3 ] = variableProduct;
+ mockStoreState.productVariations[ 301 ] = populatedVariation301;
+ mockStoreState.productVariations[ 302 ] = populatedVariation302;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 3,
selectedAttributes: [
{ attribute: 'attribute_pa_color', value: 'Blue' },
@@ -425,8 +378,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
describe( 'multi-word attribute names', () => {
it( 'matches when selected attributes use hyphenated slugs', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variableProduct = {
id: 3,
type: 'variable',
@@ -451,11 +402,11 @@ describe( 'woocommerce/products store – product context derived state', () =>
id: 301,
name: 'Blue 42',
} as ProductResponseItem;
- mockRegisteredStore!.state.products[ 3 ] = variableProduct;
- mockRegisteredStore!.state.productVariations[ 301 ] =
+ mockStoreState.products[ 3 ] = variableProduct;
+ mockStoreState.productVariations[ 301 ] =
populatedVariation;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 3,
selectedAttributes: [
{
@@ -475,8 +426,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
describe( 'Any attribute handling', () => {
it( 'matches variation with "Any" attribute when value is selected', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variableProduct = {
id: 2,
type: 'variable',
@@ -501,11 +450,11 @@ describe( 'woocommerce/products store – product context derived state', () =>
id: 201,
name: 'Any Color Small',
} as ProductResponseItem;
- mockRegisteredStore!.state.products[ 2 ] = variableProduct;
- mockRegisteredStore!.state.productVariations[ 201 ] =
+ mockStoreState.products[ 2 ] = variableProduct;
+ mockStoreState.productVariations[ 201 ] =
populatedVariation;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 2,
selectedAttributes: [
{ attribute: 'Color', value: 'Red' },
@@ -517,8 +466,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'does not match "Any" attribute when selected value is null', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variableProduct = {
id: 2,
type: 'variable',
@@ -532,9 +479,9 @@ describe( 'woocommerce/products store – product context derived state', () =>
},
],
} as unknown as ProductResponseItem;
- mockRegisteredStore!.state.products[ 2 ] = variableProduct;
+ mockStoreState.products[ 2 ] = variableProduct;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 2,
selectedAttributes: [
{
@@ -549,8 +496,6 @@ describe( 'woocommerce/products store – product context derived state', () =>
} );
it( 'does not match "Any" attribute when attribute is not selected', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
const variableProduct = {
id: 2,
type: 'variable',
@@ -564,9 +509,9 @@ describe( 'woocommerce/products store – product context derived state', () =>
},
],
} as unknown as ProductResponseItem;
- mockRegisteredStore!.state.products[ 2 ] = variableProduct;
+ mockStoreState.products[ 2 ] = variableProduct;
- const result = mockRegisteredStore!.state.findProduct( {
+ const result = mockStoreState.findProduct( {
id: 2,
selectedAttributes: [
{ attribute: 'Size', value: 'Small' },
@@ -581,123 +526,87 @@ describe( 'woocommerce/products store – product context derived state', () =>
describe( 'productInContext', () => {
it( 'returns product when variationId is null (simple product path)', () => {
- expect( mockRegisteredStore ).not.toBeNull();
+ mockStoreState.productId = 42;
+ mockStoreState.variationId = null;
- mockRegisteredStore!.state.productId = 42;
- mockRegisteredStore!.state.variationId = null;
-
- expect( mockRegisteredStore!.state.productInContext ).toBe(
- mockProduct
- );
+ expect( mockStoreState.productInContext ).toBe( mockProduct );
} );
it( 'returns productVariationInContext when variationId is set and populated', () => {
- expect( mockRegisteredStore ).not.toBeNull();
+ mockStoreState.productId = 42;
+ mockStoreState.variationId = 99;
- mockRegisteredStore!.state.productId = 42;
- mockRegisteredStore!.state.variationId = 99;
-
- expect( mockRegisteredStore!.state.productInContext ).toBe(
- mockVariation
- );
+ expect( mockStoreState.productInContext ).toBe( mockVariation );
} );
it( 'falls back to product when variation is missing from productVariations', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- mockRegisteredStore!.state.productId = 42;
- mockRegisteredStore!.state.variationId = 123;
+ mockStoreState.productId = 42;
+ mockStoreState.variationId = 123;
- expect( mockRegisteredStore!.state.productInContext ).toBe(
- mockProduct
- );
+ expect( mockStoreState.productInContext ).toBe( mockProduct );
} );
it( 'returns null when neither product nor variation resolves', () => {
- expect( mockRegisteredStore ).not.toBeNull();
+ mockStoreState.productId = 0;
+ mockStoreState.variationId = null;
- mockRegisteredStore!.state.productId = 0;
- mockRegisteredStore!.state.variationId = null;
-
- expect( mockRegisteredStore!.state.productInContext ).toBeNull();
+ expect( mockStoreState.productInContext ).toBeNull();
} );
it( 'honors local context over state IDs', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- mockRegisteredStore!.state.productId = 1;
- mockRegisteredStore!.state.variationId = null;
+ mockStoreState.productId = 1;
+ mockStoreState.variationId = null;
mockContext = { productId: 42, variationId: 99 };
- expect( mockRegisteredStore!.state.productInContext ).toBe(
- mockVariation
- );
+ expect( mockStoreState.productInContext ).toBe( mockVariation );
} );
} );
describe( 'Product block path (context without variationId)', () => {
it( 'mainProductInContext reads productId from context', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
mockContext = { productId: 42 };
- mockRegisteredStore!.state.variationId = null;
+ mockStoreState.variationId = null;
- expect( mockRegisteredStore!.state.mainProductInContext ).toBe(
- mockProduct
- );
+ expect( mockStoreState.mainProductInContext ).toBe( mockProduct );
} );
it( 'productVariationInContext reads variationId from context when available', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
mockContext = { productId: 42, variationId: 99 };
- mockRegisteredStore!.state.variationId = null;
+ mockStoreState.variationId = null;
- expect( mockRegisteredStore!.state.productVariationInContext ).toBe(
+ expect( mockStoreState.productVariationInContext ).toBe(
mockVariation
);
} );
it( 'productVariationInContext falls back to state when context exists but does not define variationId', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
mockContext = { productId: 42 };
- mockRegisteredStore!.state.variationId = 99;
+ mockStoreState.variationId = 99;
- expect( mockRegisteredStore!.state.productVariationInContext ).toBe(
+ expect( mockStoreState.productVariationInContext ).toBe(
mockVariation
);
} );
it( 'productVariationInContext does not fall back to state when context explicitly sets variationId to null', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
mockContext = { productId: 42, variationId: null };
- mockRegisteredStore!.state.variationId = 99;
+ mockStoreState.variationId = 99;
- expect( mockRegisteredStore!.state.productVariationInContext ).toBe(
- null
- );
+ expect( mockStoreState.productVariationInContext ).toBe( null );
} );
it( 'productVariationInContext falls back to state when context does not exist', () => {
- expect( mockRegisteredStore ).not.toBeNull();
+ mockStoreState.variationId = 99;
- mockRegisteredStore!.state.variationId = 99;
-
- expect( mockRegisteredStore!.state.productVariationInContext ).toBe(
+ expect( mockStoreState.productVariationInContext ).toBe(
mockVariation
);
} );
it( 'productVariationInContext returns null when both context and state variationId are null', () => {
- expect( mockRegisteredStore ).not.toBeNull();
-
- mockRegisteredStore!.state.variationId = null;
+ mockStoreState.variationId = null;
- expect(
- mockRegisteredStore!.state.productVariationInContext
- ).toBeNull();
+ expect( mockStoreState.productVariationInContext ).toBeNull();
} );
} );
} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts
index f73f4dcfbdc..6237aadc042 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts
@@ -172,7 +172,7 @@ const focusableSelectors = `
const getFocusableElements = ( container: HTMLElement | null ) =>
container
? Array.from(
- container!.querySelectorAll< HTMLElement >( focusableSelectors )
+ container.querySelectorAll< HTMLElement >( focusableSelectors )
).filter( ( el ) => el.offsetParent !== null )
: [];
@@ -292,7 +292,7 @@ store< MiniCart >(
return state.productCountColor;
}
const { ref } = getElement();
- return getClosestColor( ref!, 'color' ) || '#000';
+ return getClosestColor( ref, 'color' ) || '#000';
}
},
@@ -337,9 +337,10 @@ store< MiniCart >(
if ( e.key === 'Tab' ) {
const { ref } = getElement();
const focusableElements = getFocusableElements( ref );
+ const activeElement = ref.ownerDocument.activeElement;
if (
e.shiftKey &&
- document.activeElement === focusableElements?.[ 0 ]
+ activeElement === focusableElements?.[ 0 ]
) {
// Focus last element when shift+tab in the first one.
e.preventDefault();
@@ -348,7 +349,7 @@ store< MiniCart >(
]?.focus();
} else if (
! e.shiftKey &&
- document.activeElement ===
+ activeElement ===
focusableElements?.[
focusableElements.length - 1
]
diff --git a/plugins/woocommerce/client/blocks/packages/components/form-step/test/index.tsx b/plugins/woocommerce/client/blocks/packages/components/form-step/test/index.tsx
index 4086ebfb2b0..7fcbc62bc83 100644
--- a/plugins/woocommerce/client/blocks/packages/components/form-step/test/index.tsx
+++ b/plugins/woocommerce/client/blocks/packages/components/form-step/test/index.tsx
@@ -21,12 +21,9 @@ describe( 'FormStep', () => {
</FormStep>
);
- const element = container.querySelector( '#my-id' );
-
- expect( element ).toBeDefined();
- // Look eslint and typescript, we're checking it above
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- expect( element!.classList.contains( 'my-classname' ) ).toBeTruthy();
+ expect( container.querySelector( '#my-id' ) ).toHaveClass(
+ 'my-classname'
+ );
} );
test( 'should render a fieldset if a legend is provided', () => {