Skip to content

Commit

Permalink
Merge pull request #407 from Workiva/lazy-tests-and-wrapper
Browse files Browse the repository at this point in the history
Add lazy test coverage, add wrapper in react-dart
  • Loading branch information
rm-astro-wf authored Sep 30, 2024
2 parents e7741ae + 94aa9f3 commit 7564c85
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 22 deletions.
17 changes: 15 additions & 2 deletions lib/react_client/react_interop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -312,14 +312,27 @@ ReactComponentFactoryProxy lazy(Future<ReactComponentFactoryProxy> Function() lo
() => futureToPromise(
(() async {
final factory = await load();
return jsify({'default': factory.type});
// By using a wrapper uiForwardRef it ensures that we have a matching factory proxy type given to react-dart's lazy,
// a `ReactDartWrappedComponentFactoryProxy`. This is necessary to have consistent prop conversions since we don't
// have access to the original factory proxy outside of this async block.
final wrapper = forwardRef2((props, ref) {
final children = props['children'];
return factory.build(
{...props, 'ref': ref},
[
if (children != null && !(children is List && children.isEmpty)) children,
],
);
});
return jsify({'default': wrapper.type});
})(),
),
),
);

// Setting this version and wrapping with ReactDartWrappedComponentFactoryProxy
// is only okay because it matches the version and factory proxy of the wrapperFactory above.
setProperty(hoc, 'dartComponentVersion', ReactDartComponentVersion.component2);

return ReactDartWrappedComponentFactoryProxy(hoc);
}

Expand Down
37 changes: 25 additions & 12 deletions test/factory/common_factory_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import '../util.dart';
void commonFactoryTests(ReactComponentFactoryProxy factory,
{String? dartComponentVersion,
bool skipPropValuesTest = false,
bool isNonDartComponentWithDartWrapper = false,
ReactElement Function(dynamic children)? renderWrapper}) {
_childKeyWarningTests(
factory,
Expand Down Expand Up @@ -115,7 +116,7 @@ void commonFactoryTests(ReactComponentFactoryProxy factory,
shouldAlwaysBeList: isDartComponent2(factory({})));
});

if (isDartComponent(factory({}))) {
if (isDartComponent(factory({})) && !isNonDartComponentWithDartWrapper) {
group('passes children to the Dart component when specified as', () {
final notCalledSentinelValue = Object();
dynamic childrenFromLastRender;
Expand Down Expand Up @@ -173,7 +174,7 @@ void commonFactoryTests(ReactComponentFactoryProxy factory,
}
}

if (isDartComponent2(factory({}))) {
if (isDartComponent2(factory({})) && !isNonDartComponentWithDartWrapper) {
test('executes Dart render code in the component zone', () {
final oldComponentZone = componentZone;
addTearDown(() => componentZone = oldComponentZone);
Expand All @@ -193,7 +194,10 @@ void commonFactoryTests(ReactComponentFactoryProxy factory,
}
}

void domEventHandlerWrappingTests(ReactComponentFactoryProxy factory) {
void domEventHandlerWrappingTests(
ReactComponentFactoryProxy factory, {
bool isNonDartComponentWithDartWrapper = false,
}) {
Element renderAndGetRootNode(ReactElement content) {
final mountNode = Element.div();
react_dom.render(content, mountNode);
Expand Down Expand Up @@ -270,22 +274,31 @@ void domEventHandlerWrappingTests(ReactComponentFactoryProxy factory) {
}
});

if (isDartComponent(factory({}))) {
if (isDartComponent(factory({})) && !isNonDartComponentWithDartWrapper) {
group('in a way that the handlers are callable from within the Dart component:', () {
setUpAll(() {
expect(propsFromDartRender, isNotNull,
reason: 'test setup: component must pass props into props.onDartRender');
});

late react.SyntheticMouseEvent event;
final divRef = react.createRef<DivElement>();
render(react.div({
'ref': divRef,
'onClick': (react.SyntheticMouseEvent e) => event = e,
}));
rtu.Simulate.click(divRef);
late react.SyntheticMouseEvent dummyEvent;
setUpAll(() {
final mountNode = DivElement();
document.body!.append(mountNode);
addTearDown(() {
react_dom.unmountComponentAtNode(mountNode);
mountNode.remove();
});

final dummyEvent = event;
final divRef = react.createRef<DivElement>();
react_dom.render(
react.div({
'ref': divRef,
'onClick': (react.SyntheticMouseEvent e) => dummyEvent = e,
}),
mountNode);
divRef.current!.click();
});

for (final eventCase in eventCases.where((helper) => helper.isDart)) {
test(eventCase.description, () {
Expand Down
69 changes: 61 additions & 8 deletions test/react_lazy_test.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,79 @@
@TestOn('browser')
@JS()
library react.react_lazy_test;

import 'dart:js_util';

import 'package:js/js.dart';
import 'package:react/hooks.dart';
import 'package:react/react.dart' as react;
import 'package:react/react_client/component_factory.dart';
import 'package:react/react_client/react_interop.dart';
import 'package:test/test.dart';

import 'factory/common_factory_tests.dart';

main() {
group('lazy', () {
group('- common factory behavior -', () {
final LazyTest = react.lazy(() async => react.registerFunctionComponent((props) {
group('Dart component', () {
final LazyTest = react.lazy(() async => react.forwardRef2((props, ref) {
useImperativeHandle(ref, () => TestImperativeHandle());
props['onDartRender']?.call(props);
return react.div({...props});
}));

commonFactoryTests(
LazyTest,
// ignore: invalid_use_of_protected_member
dartComponentVersion: ReactDartComponentVersion.component2,
renderWrapper: (child) => react.Suspense({'fallback': 'Loading...'}, child),
);
group('- common factory behavior -', () {
commonFactoryTests(
LazyTest,
// ignore: invalid_use_of_protected_member
dartComponentVersion: ReactDartComponentVersion.component2,
renderWrapper: (child) => react.Suspense({'fallback': 'Loading...'}, child),
);
});

group('- dom event handler wrapping -', () {
domEventHandlerWrappingTests(LazyTest);
});

group('- refs -', () {
refTests<TestImperativeHandle>(LazyTest, verifyRefValue: (ref) {
expect(ref, isA<TestImperativeHandle>());
});
});
});

group('JS component', () {
final LazyJsTest = react.lazy(() async => ReactJsComponentFactoryProxy(_JsFoo));

group('- common factory behavior -', () {
commonFactoryTests(
LazyJsTest,
// ignore: invalid_use_of_protected_member
dartComponentVersion: ReactDartComponentVersion.component2,
// This isn't a Dart component, but it's detected as one by tests due to the factory's dartComponentVersion
isNonDartComponentWithDartWrapper: true,
renderWrapper: (child) => react.Suspense({'fallback': 'Loading...'}, child),
);
});

group('- dom event handler wrapping -', () {
domEventHandlerWrappingTests(
LazyJsTest,
// This isn't a Dart component, but it's detected as one by tests due to the factory's dartComponentVersion
isNonDartComponentWithDartWrapper: true,
);
});

group('- refs -', () {
refTests<ReactComponent>(LazyJsTest, verifyRefValue: (ref) {
expect(getProperty(ref as Object, 'constructor'), same(_JsFoo));
});
});
});
});
}

class TestImperativeHandle {}

@JS()
external ReactClass get _JsFoo;
1 change: 1 addition & 0 deletions test/react_lazy_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<script src="packages/react/react_dom.js"></script>
<link rel="x-dart-test" href="react_lazy_test.dart" />
<script src="packages/test/dart.js"></script>
<script src="factory/js_factory_test.js"></script>
</head>
<body></body>
</html>

0 comments on commit 7564c85

Please sign in to comment.