@khanacademy/wonder-blocks-testing 9.1.0 → 9.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @khanacademy/wonder-blocks-testing
2
2
 
3
+ ## 9.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f530fbeb: Fix bug where the test harness was causing the component under test to be remounted when rerendering instead of reusing the existing instance
8
+
9
+ ## 9.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 76883b4e: Make sure Wonder Blocks Testing is dependent on Core
14
+
15
+ ### Patch Changes
16
+
17
+ - 76883b4e: Output the adapter name with the Adapter component
18
+ - Updated dependencies [f19da46e]
19
+ - @khanacademy/wonder-blocks-core@6.0.2
20
+ - @khanacademy/wonder-blocks-data@13.0.1
21
+
3
22
  ## 9.1.0
4
23
 
5
24
  ### Minor Changes
package/dist/es/index.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import * as React from 'react';
2
- import { useContext } from 'react';
3
2
  import { action } from '@storybook/addon-actions';
4
3
  import { InterceptRequests } from '@khanacademy/wonder-blocks-data';
5
4
  import { StaticRouter, MemoryRouter, Switch, Route } from 'react-router-dom';
6
5
  import { KindError, Errors } from '@khanacademy/wonder-stuff-core';
7
- import { StyleSheet, css } from 'aphrodite';
6
+ import { RenderStateRoot } from '@khanacademy/wonder-blocks-core';
8
7
 
9
8
  const fixtures = Component => {
10
9
  const templateMap = new WeakMap();
@@ -361,381 +360,6 @@ const adapter$1 = (children, config) => {
361
360
  return React.createElement(MemoryRouter, routerProps, wrappedWithRoute);
362
361
  };
363
362
 
364
- function _extends$1() {
365
- _extends$1 = Object.assign ? Object.assign.bind() : function (target) {
366
- for (var i = 1; i < arguments.length; i++) {
367
- var source = arguments[i];
368
- for (var key in source) {
369
- if (Object.prototype.hasOwnProperty.call(source, key)) {
370
- target[key] = source[key];
371
- }
372
- }
373
- }
374
- return target;
375
- };
376
- return _extends$1.apply(this, arguments);
377
- }
378
- function _objectWithoutPropertiesLoose(source, excluded) {
379
- if (source == null) return {};
380
- var target = {};
381
- var sourceKeys = Object.keys(source);
382
- var key, i;
383
- for (i = 0; i < sourceKeys.length; i++) {
384
- key = sourceKeys[i];
385
- if (excluded.indexOf(key) >= 0) continue;
386
- target[key] = source[key];
387
- }
388
- return target;
389
- }
390
- function flatten(list) {
391
- const result = [];
392
- if (!list) {
393
- return result;
394
- } else if (Array.isArray(list)) {
395
- for (const item of list) {
396
- result.push(...flatten(item));
397
- }
398
- } else {
399
- result.push(list);
400
- }
401
- return result;
402
- }
403
- function processStyleList(style) {
404
- const stylesheetStyles = [];
405
- const inlineStyles = [];
406
- if (!style) {
407
- return {
408
- style: {},
409
- className: ""
410
- };
411
- }
412
- const shouldInlineStyles = typeof global !== "undefined" && global.SNAPSHOT_INLINE_APHRODITE;
413
- flatten(style).forEach(child => {
414
- const _definition = child._definition;
415
- if (_definition != null) {
416
- if (shouldInlineStyles) {
417
- const def = {};
418
- for (const [key, value] of Object.entries(_definition)) {
419
- def[key.replace(/-[a-z]/g, match => match[1].toUpperCase())] = value;
420
- }
421
- inlineStyles.push(def);
422
- } else {
423
- stylesheetStyles.push(child);
424
- }
425
- } else {
426
- inlineStyles.push(child);
427
- }
428
- });
429
- const inlineStylesObject = Object.assign({}, ...inlineStyles);
430
- if (inlineStyles.length > 0 && !shouldInlineStyles) {
431
- const inlineStylesStyleSheet = StyleSheet.create({
432
- inlineStyles: inlineStylesObject
433
- });
434
- stylesheetStyles.push(inlineStylesStyleSheet.inlineStyles);
435
- }
436
- return {
437
- style: shouldInlineStyles ? inlineStylesObject : {},
438
- className: css(...stylesheetStyles)
439
- };
440
- }
441
- const _excluded$2 = ["children", "style", "tag", "testId"];
442
- const isHeaderRegex = /^h[1-6]$/;
443
- const styles$1 = StyleSheet.create({
444
- text: {
445
- WebkitFontSmoothing: "antialiased",
446
- MozOsxFontSmoothing: "grayscale"
447
- },
448
- header: {
449
- marginTop: 0,
450
- marginBottom: 0
451
- }
452
- });
453
- React.forwardRef(function Text(_ref, ref) {
454
- let {
455
- children,
456
- style,
457
- tag: Tag = "span",
458
- testId
459
- } = _ref,
460
- otherProps = _objectWithoutPropertiesLoose(_ref, _excluded$2);
461
- const isHeader = isHeaderRegex.test(Tag);
462
- const styleAttributes = processStyleList([styles$1.text, isHeader && styles$1.header, style]);
463
- return React.createElement(Tag, _extends$1({}, otherProps, {
464
- style: styleAttributes.style,
465
- className: styleAttributes.className,
466
- "data-test-id": testId,
467
- ref: ref
468
- }), children);
469
- });
470
- const _excluded$1 = ["className", "style"];
471
- function addStyle(Component, defaultStyle) {
472
- return React.forwardRef((props, ref) => {
473
- const {
474
- className,
475
- style
476
- } = props,
477
- otherProps = _objectWithoutPropertiesLoose(props, _excluded$1);
478
- const reset = typeof Component === "string" ? overrides[Component] : null;
479
- const {
480
- className: aphroditeClassName,
481
- style: inlineStyles
482
- } = processStyleList([reset, defaultStyle, style]);
483
- return React.createElement(Component, _extends$1({}, otherProps, {
484
- ref: ref,
485
- className: [aphroditeClassName, className].filter(Boolean).join(" "),
486
- style: inlineStyles
487
- }));
488
- });
489
- }
490
- const overrides = StyleSheet.create({
491
- button: {
492
- margin: 0,
493
- "::-moz-focus-inner": {
494
- border: 0
495
- }
496
- }
497
- });
498
- const _excluded = ["testId", "tag"];
499
- const styles = StyleSheet.create({
500
- default: {
501
- alignItems: "stretch",
502
- borderWidth: 0,
503
- borderStyle: "solid",
504
- boxSizing: "border-box",
505
- display: "flex",
506
- flexDirection: "column",
507
- margin: 0,
508
- padding: 0,
509
- position: "relative",
510
- zIndex: 0,
511
- minHeight: 0,
512
- minWidth: 0
513
- }
514
- });
515
- const StyledDiv = addStyle("div", styles.default);
516
- const StyledArticle = addStyle("article", styles.default);
517
- const StyledAside = addStyle("aside", styles.default);
518
- const StyledNav = addStyle("nav", styles.default);
519
- const StyledSection = addStyle("section", styles.default);
520
- React.forwardRef(function View(props, ref) {
521
- const {
522
- testId,
523
- tag = "div"
524
- } = props,
525
- restProps = _objectWithoutPropertiesLoose(props, _excluded);
526
- const commonProps = _extends$1({}, restProps, {
527
- "data-test-id": testId
528
- });
529
- switch (tag) {
530
- case "article":
531
- return React.createElement(StyledArticle, _extends$1({}, commonProps, {
532
- ref: ref
533
- }));
534
- case "aside":
535
- return React.createElement(StyledAside, _extends$1({}, commonProps, {
536
- ref: ref
537
- }));
538
- case "nav":
539
- return React.createElement(StyledNav, _extends$1({}, commonProps, {
540
- ref: ref
541
- }));
542
- case "section":
543
- return React.createElement(StyledSection, _extends$1({}, commonProps, {
544
- ref: ref
545
- }));
546
- case "div":
547
- return React.createElement(StyledDiv, _extends$1({}, commonProps, {
548
- ref: ref
549
- }));
550
- default:
551
- throw Error(`${tag} is not an allowed value for the 'tag' prop`);
552
- }
553
- });
554
- let RenderState = function (RenderState) {
555
- RenderState["Root"] = "root";
556
- RenderState["Initial"] = "initial";
557
- RenderState["Standard"] = "standard";
558
- return RenderState;
559
- }({});
560
- const RenderStateContext = React.createContext(RenderState.Root);
561
- RenderStateContext.displayName = "RenderStateContext";
562
- class WithSSRPlaceholder extends React.Component {
563
- constructor(...args) {
564
- super(...args);
565
- this.state = {
566
- mounted: false
567
- };
568
- this._isTheRootComponent = false;
569
- }
570
- componentDidMount() {
571
- if (this._isTheRootComponent) {
572
- this.setState({
573
- mounted: true
574
- });
575
- }
576
- }
577
- _renderAsRootComponent() {
578
- const {
579
- mounted
580
- } = this.state;
581
- const {
582
- children,
583
- placeholder
584
- } = this.props;
585
- this._isTheRootComponent = true;
586
- if (mounted) {
587
- return React.createElement(RenderStateContext.Provider, {
588
- value: RenderState.Standard
589
- }, children());
590
- }
591
- if (placeholder) {
592
- return React.createElement(RenderStateContext.Provider, {
593
- value: RenderState.Initial
594
- }, placeholder());
595
- }
596
- return null;
597
- }
598
- _maybeRender(renderState) {
599
- const {
600
- children,
601
- placeholder
602
- } = this.props;
603
- switch (renderState) {
604
- case RenderState.Root:
605
- return this._renderAsRootComponent();
606
- case RenderState.Initial:
607
- if (placeholder) {
608
- return placeholder();
609
- }
610
- return null;
611
- case RenderState.Standard:
612
- return children();
613
- }
614
- {
615
- var _JSON$stringify;
616
- console.log(`We got a render state we don't understand: "${(_JSON$stringify = JSON.stringify(renderState)) != null ? _JSON$stringify : ""}"`);
617
- return this._maybeRender(RenderState.Root);
618
- }
619
- }
620
- render() {
621
- return React.createElement(RenderStateContext.Consumer, null, value => this._maybeRender(value));
622
- }
623
- }
624
- class UniqueIDFactory {
625
- constructor(scope) {
626
- this._uniqueFactoryName = void 0;
627
- this.get = key => {
628
- const normalizedKey = key.toLowerCase();
629
- if (!this._hasValidIdChars(key)) {
630
- throw new Error(`Invalid identifier key: ${key}`);
631
- }
632
- return `${this._uniqueFactoryName}-${normalizedKey}`;
633
- };
634
- scope = typeof scope === "string" ? scope : "";
635
- const normalizedScope = scope.toLowerCase();
636
- if (!this._hasValidIdChars(normalizedScope)) {
637
- throw new Error(`Invalid factory scope: ${scope}`);
638
- }
639
- this._uniqueFactoryName = `uid-${normalizedScope}-${UniqueIDFactory._factoryUniquenessCounter++}`;
640
- }
641
- _hasValidIdChars(value) {
642
- if (typeof value !== "string") {
643
- return false;
644
- }
645
- const invalidCharsReplaced = value.replace(/[^\d\w-]/g, "-");
646
- return value === invalidCharsReplaced;
647
- }
648
- }
649
- UniqueIDFactory._factoryUniquenessCounter = 0;
650
- class SsrIDFactory {
651
- get(id) {
652
- return id;
653
- }
654
- }
655
- SsrIDFactory.Default = new SsrIDFactory();
656
- var SsrIDFactory$1 = SsrIDFactory.Default;
657
- class UniqueIDProvider extends React.Component {
658
- constructor(...args) {
659
- super(...args);
660
- this._idFactory = void 0;
661
- }
662
- _performRender(firstRender) {
663
- const {
664
- children,
665
- mockOnFirstRender,
666
- scope
667
- } = this.props;
668
- if (firstRender) {
669
- if (mockOnFirstRender) {
670
- return children(SsrIDFactory$1);
671
- }
672
- return null;
673
- }
674
- if (!this._idFactory) {
675
- this._idFactory = new UniqueIDFactory(scope);
676
- }
677
- return children(this._idFactory);
678
- }
679
- render() {
680
- return React.createElement(WithSSRPlaceholder, {
681
- placeholder: () => this._performRender(true)
682
- }, () => this._performRender(false));
683
- }
684
- }
685
- class IDProvider extends React.Component {
686
- renderChildren(ids) {
687
- const {
688
- id,
689
- children
690
- } = this.props;
691
- const uniqueId = ids ? ids.get(IDProvider.defaultId) : id;
692
- if (!uniqueId) {
693
- throw new Error("Did not get an identifier factory nor a id prop");
694
- }
695
- return children(uniqueId);
696
- }
697
- render() {
698
- const {
699
- id,
700
- scope
701
- } = this.props;
702
- if (id) {
703
- return this.renderChildren();
704
- } else {
705
- return React.createElement(UniqueIDProvider, {
706
- scope: scope,
707
- mockOnFirstRender: true
708
- }, ids => this.renderChildren(ids));
709
- }
710
- }
711
- }
712
- IDProvider.defaultId = "wb-id";
713
- const useRenderState = () => useContext(RenderStateContext);
714
- const {
715
- useEffect,
716
- useState
717
- } = React;
718
- const RenderStateRoot = ({
719
- children,
720
- throwIfNested: _throwIfNested = true
721
- }) => {
722
- const [firstRender, setFirstRender] = useState(true);
723
- const renderState = useRenderState();
724
- useEffect(() => {
725
- setFirstRender(false);
726
- }, []);
727
- if (renderState !== RenderState.Root) {
728
- if (_throwIfNested) {
729
- throw new Error("There's already a <RenderStateRoot> above this instance in " + "the render tree. This instance should be removed.");
730
- }
731
- return React.createElement(React.Fragment, null, children);
732
- }
733
- const value = firstRender ? RenderState.Initial : RenderState.Standard;
734
- return React.createElement(RenderStateContext.Provider, {
735
- value: value
736
- }, children);
737
- };
738
-
739
363
  const defaultConfig = null;
740
364
  const adapter = (children, config) => {
741
365
  if (config !== true) {
@@ -784,15 +408,20 @@ function _extends() {
784
408
  return _extends.apply(this, arguments);
785
409
  }
786
410
 
787
- const Adapter = ({
788
- children,
789
- adapter,
790
- config
791
- }) => {
792
- if (config == null) {
793
- return React.createElement(React.Fragment, null, children);
794
- }
795
- return adapter(children, config);
411
+ const componentCache = new Map();
412
+ const getNamedAdapterComponent = name => {
413
+ const existing = componentCache.get(name);
414
+ if (existing != null) {
415
+ return existing;
416
+ }
417
+ const newComponent = ({
418
+ children,
419
+ config,
420
+ adapter
421
+ }) => adapter(children, config);
422
+ newComponent.displayName = `Adapter(${name})`;
423
+ componentCache.set(name, newComponent);
424
+ return newComponent;
796
425
  };
797
426
 
798
427
  const Adapt = ({
@@ -800,21 +429,18 @@ const Adapt = ({
800
429
  adapters,
801
430
  configs
802
431
  }) => {
803
- const thisAdapterName = Object.keys(adapters).at(-1);
804
- if (thisAdapterName == null) {
805
- return React.createElement(React.Fragment, null, children);
806
- }
807
- const thisAdapter = adapters[thisAdapterName];
808
- const thisConfig = configs[thisAdapterName];
809
- const restAdapters = Object.fromEntries(Object.entries(adapters).slice(0, -1));
810
- const restConfigs = Object.fromEntries(Object.entries(configs).filter(([name]) => name !== thisAdapterName));
811
- return React.createElement(Adapter, {
812
- adapter: thisAdapter,
813
- config: thisConfig
814
- }, React.createElement(Adapt, {
815
- adapters: restAdapters,
816
- configs: restConfigs
817
- }, children));
432
+ return Object.entries(adapters).reduce((newChildren, [name, adapter]) => {
433
+ const theConfig = configs[name];
434
+ if (theConfig == null) {
435
+ return newChildren;
436
+ }
437
+ const Adapter = getNamedAdapterComponent(name);
438
+ return React.createElement(Adapter, {
439
+ key: name,
440
+ adapter: adapter,
441
+ config: theConfig
442
+ }, newChildren);
443
+ }, React.createElement(React.Fragment, null, children));
818
444
  };
819
445
 
820
446
  const makeTestHarness = (adapters, defaultConfigs) => {
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ import type { TestHarnessAdapter } from "./types";
3
+ type Props<TConfig = any> = {
4
+ children: React.ReactNode;
5
+ config: TConfig;
6
+ adapter: TestHarnessAdapter<TConfig>;
7
+ };
8
+ /**
9
+ * Get a component tagged with the given name for rendering an adapter.
10
+ *
11
+ * We can share these across invocations because only the name is used.
12
+ * The rest is configured at render time. This way we don't recreate new
13
+ * components on the fly and cause remounting to occur.
14
+ */
15
+ export declare const getNamedAdapterComponent: (name: string) => React.FunctionComponent<Props<any>>;
16
+ export {};