function toHaveSpyInteractions(matchersUtil) {
      return {
        compare: function (actual) {
          const result = {};

          if (!j$.isObject_(actual)) {
            throw new Error(
              getErrorMsg('Expected a spy object, but got ' + typeof actual + '.'),
            );
          }

          if (arguments.length > 1) {
            throw new Error(getErrorMsg('Does not take arguments'));
          }

          result.pass = false;
          let hasSpy = false;
          const calledSpies = [];
          for (const spy of Object.values(actual)) {
            if (!j$.isSpy(spy)) continue;
            hasSpy = true;

            if (spy.calls.any()) {
              result.pass = true;
              calledSpies.push([spy.and.identity, spy.calls.count()]);
            }
          }

          if (!hasSpy) {
            throw new Error(
              getErrorMsg('Expected a spy object with spies, but object has no spies.'),
            );
          }

          let resultMessage;
          if (result.pass) {
            resultMessage =
              'Expected spy object spies not to have been called, ' +
              'but the following spies were called: ';
            resultMessage += calledSpies
              .map(([spyName, spyCount]) => {
                return `${spyName} called ${spyCount} time(s)`;
              })
              .join(', ');
          } else {
            resultMessage =
              'Expected spy object spies to have been called, ' +
              'but no spies were called.';
          }
          result.message = resultMessage;

          return result;
        },
      };
    }
{@link expect} the actual (a {@link SpyObj}) spies to have been called.
@function
@name matchers#toHaveSpyInteractions
@since 4.1.0
@example
expect(mySpyObj).toHaveSpyInteractions();
expect(mySpyObj).not.toHaveSpyInteractions();