Best JavaScript code snippet using fast-check-monorepo
uniqueArray.spec.ts
Source:uniqueArray.spec.ts
1import * as fc from 'fast-check';2import { uniqueArray, UniqueArrayConstraints } from '../../../src/arbitrary/uniqueArray';3import { FakeIntegerArbitrary, fakeArbitrary } from './__test-helpers__/ArbitraryHelpers';4import * as ArrayArbitraryMock from '../../../src/arbitrary/_internals/ArrayArbitrary';5import {6 assertProduceCorrectValues,7 assertProduceSameValueGivenSameSeed,8 assertProduceValuesShrinkableWithoutContext,9 assertShrinkProducesSameValueWithoutInitialContext,10} from './__test-helpers__/ArbitraryAssertions';11import { Value } from '../../../src/check/arbitrary/definition/Value';12import { buildShrinkTree, renderTree } from './__test-helpers__/ShrinkTree';13import { sizeRelatedGlobalConfigArb } from './__test-helpers__/SizeHelpers';14import { withConfiguredGlobal } from './__test-helpers__/GlobalSettingsHelpers';15function beforeEachHook() {16 jest.resetModules();17 jest.restoreAllMocks();18 fc.configureGlobal({ beforeEach: beforeEachHook });19}20beforeEach(beforeEachHook);21describe('uniqueArray', () => {22 it('should instantiate ArrayArbitrary(arb, 0, ?, 0x7fffffff, n.a, <default>) for uniqueArray(arb)', () => {23 fc.assert(24 fc.property(sizeRelatedGlobalConfigArb, (config) => {25 // Arrange26 const { instance: childInstance } = fakeArbitrary<unknown>();27 const { instance } = fakeArbitrary<unknown[]>();28 const ArrayArbitrary = jest.spyOn(ArrayArbitraryMock, 'ArrayArbitrary');29 ArrayArbitrary.mockImplementation(() => instance as ArrayArbitraryMock.ArrayArbitrary<unknown>);30 // Act31 const arb = withConfiguredGlobal(config, () => uniqueArray(childInstance));32 // Assert33 expect(ArrayArbitrary).toHaveBeenCalledWith(34 childInstance,35 0,36 expect.any(Number),37 0x7fffffff,38 undefined,39 expect.any(Function),40 []41 );42 const receivedGeneratedMaxLength = ArrayArbitrary.mock.calls[0][2]; // Expecting the real value would check an implementation detail43 expect(receivedGeneratedMaxLength).toBeGreaterThan(0);44 expect(receivedGeneratedMaxLength).toBeLessThanOrEqual(2 ** 31 - 1);45 expect(Number.isInteger(receivedGeneratedMaxLength)).toBe(true);46 expect(arb).toBe(instance);47 })48 );49 });50 it('should instantiate ArrayArbitrary(arb, 0, ?, maxLength, n.a, <default>) for uniqueArray(set, {maxLength})', () => {51 fc.assert(52 fc.property(sizeRelatedGlobalConfigArb, fc.nat({ max: 2 ** 31 - 1 }), (config, maxLength) => {53 // Arrange54 const { instance: childInstance } = fakeArbitrary<unknown>();55 const { instance } = fakeArbitrary<unknown[]>();56 const ArrayArbitrary = jest.spyOn(ArrayArbitraryMock, 'ArrayArbitrary');57 ArrayArbitrary.mockImplementation(() => instance as ArrayArbitraryMock.ArrayArbitrary<unknown>);58 // Act59 const arb = withConfiguredGlobal(config, () => uniqueArray(childInstance, { maxLength }));60 // Assert61 expect(ArrayArbitrary).toHaveBeenCalledWith(62 childInstance,63 0,64 expect.any(Number),65 maxLength,66 undefined,67 expect.any(Function),68 []69 );70 const receivedGeneratedMaxLength = ArrayArbitrary.mock.calls[0][2]; // Expecting the real value would check an implementation detail71 expect(receivedGeneratedMaxLength).toBeGreaterThanOrEqual(0);72 expect(receivedGeneratedMaxLength).toBeLessThanOrEqual(maxLength);73 expect(Number.isInteger(receivedGeneratedMaxLength)).toBe(true);74 if (config.defaultSizeToMaxWhenMaxSpecified) {75 expect(ArrayArbitrary).toHaveBeenCalledWith(76 childInstance,77 0,78 maxLength,79 maxLength,80 undefined,81 expect.any(Function),82 []83 );84 }85 expect(arb).toBe(instance);86 })87 );88 });89 it('should instantiate ArrayArbitrary(arb, minLength, ?, 0x7fffffff, n.a, <default>) for uniqueArray(arb, {minLength})', () => {90 fc.assert(91 fc.property(sizeRelatedGlobalConfigArb, fc.nat({ max: 2 ** 31 - 1 }), (config, minLength) => {92 // Arrange93 const { instance: childInstance } = fakeArbitrary<unknown>();94 const { instance, filter } = fakeArbitrary<unknown[]>();95 const ArrayArbitrary = jest.spyOn(ArrayArbitraryMock, 'ArrayArbitrary');96 ArrayArbitrary.mockImplementation(() => instance as ArrayArbitraryMock.ArrayArbitrary<unknown>);97 filter.mockReturnValue(instance);98 // Act99 const arb = withConfiguredGlobal(config, () => uniqueArray(childInstance, { minLength }));100 // Assert101 expect(ArrayArbitrary).toHaveBeenCalledWith(102 childInstance,103 minLength,104 expect.any(Number),105 0x7fffffff,106 undefined,107 expect.any(Function),108 []109 );110 const receivedGeneratedMaxLength = ArrayArbitrary.mock.calls[0][2]; // Expecting the real value would check an implementation detail111 if (minLength !== 2 ** 31 - 1) {112 expect(receivedGeneratedMaxLength).toBeGreaterThan(minLength);113 expect(receivedGeneratedMaxLength).toBeLessThanOrEqual(2 ** 31 - 1);114 expect(Number.isInteger(receivedGeneratedMaxLength)).toBe(true);115 } else {116 expect(receivedGeneratedMaxLength).toEqual(minLength);117 }118 expect(arb).toBe(instance);119 })120 );121 });122 it('should instantiate ArrayArbitrary(arb, minLength, ?, maxLength, n.a, <default>) for uniqueArray(arb, {minLength,maxLength})', () => {123 fc.assert(124 fc.property(125 sizeRelatedGlobalConfigArb,126 fc.nat({ max: 2 ** 31 - 1 }),127 fc.nat({ max: 2 ** 31 - 1 }),128 (config, aLength, bLength) => {129 // Arrange130 const [minLength, maxLength] = aLength < bLength ? [aLength, bLength] : [bLength, aLength];131 const { instance: childInstance } = fakeArbitrary<unknown>();132 const { instance, filter } = fakeArbitrary<unknown[]>();133 const ArrayArbitrary = jest.spyOn(ArrayArbitraryMock, 'ArrayArbitrary');134 ArrayArbitrary.mockImplementation(() => instance as ArrayArbitraryMock.ArrayArbitrary<unknown>);135 filter.mockReturnValue(instance);136 // Act137 const arb = withConfiguredGlobal(config, () => uniqueArray(childInstance, { minLength, maxLength }));138 // Assert139 expect(ArrayArbitrary).toHaveBeenCalledWith(140 childInstance,141 minLength,142 expect.any(Number),143 maxLength,144 undefined,145 expect.any(Function),146 []147 );148 const receivedGeneratedMaxLength = ArrayArbitrary.mock.calls[0][2]; // Expecting the real value would check an implementation detail149 expect(receivedGeneratedMaxLength).toBeGreaterThanOrEqual(minLength);150 expect(receivedGeneratedMaxLength).toBeLessThanOrEqual(maxLength);151 expect(Number.isInteger(receivedGeneratedMaxLength)).toBe(true);152 if (config.defaultSizeToMaxWhenMaxSpecified) {153 expect(ArrayArbitrary).toHaveBeenCalledWith(154 childInstance,155 minLength,156 maxLength,157 maxLength,158 undefined,159 expect.any(Function),160 []161 );162 }163 expect(arb).toBe(instance);164 }165 )166 );167 });168 it('should accept custom comparator or selector or both at the same time or none', () => {169 fc.assert(170 fc.property(171 sizeRelatedGlobalConfigArb,172 fc173 .record(174 {175 minLength: fc.nat({ max: 2 ** 31 - 1 }),176 maxLength: fc.nat({ max: 2 ** 31 - 1 }),177 comparator: comparatorArbitrary(),178 selector: selectorArbitrary(),179 depthIdentifier: fc.string(),180 },181 { requiredKeys: [] }182 )183 .map((constraints) =>184 constraints.minLength !== undefined &&185 constraints.maxLength !== undefined &&186 constraints.minLength > constraints.maxLength187 ? ({188 ...constraints,189 minLength: constraints.maxLength,190 maxLength: constraints.minLength,191 } as UniqueArrayConstraints<unknown, unknown>)192 : ({ ...constraints } as UniqueArrayConstraints<unknown, unknown>)193 ),194 (config, constraints) => {195 // Arrange196 const { instance: childInstance } = fakeArbitrary<unknown>();197 const { instance, filter } = fakeArbitrary<unknown[]>();198 const ArrayArbitrary = jest.spyOn(ArrayArbitraryMock, 'ArrayArbitrary');199 ArrayArbitrary.mockImplementation(() => instance as ArrayArbitraryMock.ArrayArbitrary<unknown>);200 filter.mockReturnValue(instance);201 // Act202 const arb = withConfiguredGlobal(config, () => uniqueArray(childInstance, constraints));203 // Assert204 expect(ArrayArbitrary).toHaveBeenCalledWith(205 childInstance,206 constraints.minLength !== undefined ? constraints.minLength : expect.any(Number),207 expect.any(Number),208 constraints.maxLength !== undefined ? constraints.maxLength : expect.any(Number),209 constraints.depthIdentifier,210 expect.any(Function),211 []212 );213 expect(arb).toBe(instance);214 }215 )216 );217 });218 it('should throw when minimum length is greater than maximum one', () => {219 fc.assert(220 fc.property(221 sizeRelatedGlobalConfigArb,222 fc.nat({ max: 2 ** 31 - 1 }),223 fc.nat({ max: 2 ** 31 - 1 }),224 fc.option(comparatorArbitrary(), { nil: undefined }),225 fc.option(selectorArbitrary(), { nil: undefined }),226 (config, aLength, bLength, comparator, selector) => {227 // Arrange228 fc.pre(aLength !== bLength);229 const [minLength, maxLength] = aLength < bLength ? [bLength, aLength] : [aLength, bLength];230 const { instance: childInstance } = fakeArbitrary<unknown>();231 // Act / Assert232 expect(() =>233 withConfiguredGlobal(config, () =>234 uniqueArray(childInstance, { minLength, maxLength, comparator, selector })235 )236 ).toThrowError();237 }238 )239 );240 });241});242describe('uniqueArray (integration)', () => {243 type Extra = UniqueArrayConstraints<unknown, unknown>;244 const extraParameters: fc.Arbitrary<Extra> = fc245 .tuple(246 fc.nat({ max: 5 }),247 fc.nat({ max: 30 }),248 fc.boolean(),249 fc.boolean(),250 fc.option(fc.func(fc.integer()), { nil: undefined }),251 fc.option(comparatorArbitrary(), { nil: undefined })252 )253 .map(([min, gap, withMin, withMax, selector, comparator]) => {254 // We only apply selector/comparator in case the minimal number of items requested can be reached with the selector/comparator.255 // eg.: selector = v => 0, means that we will have at most 1 value in the array, never more, so it cannot be used with min > 1256 const requestedMin = withMin ? min : 0;257 let selectorEnabled = requestedMin === 0 || selector === undefined;258 let comparatorEnabled = requestedMin === 0 || comparator === undefined;259 const sampleSize = 50;260 const sampledSelectedValues = new Set<unknown>();261 const sampledSelectedAndComparedValues: unknown[] = [];262 const resolvedSelector = resolveSelectorFunction(selector);263 const resolvedComparator = resolveComparatorFunction(comparator);264 for (let v = 0; v !== sampleSize && (!selectorEnabled || !comparatorEnabled); ++v) {265 const selected = resolvedSelector(v); // either v (integer in 0..sampleSize) or an integer (by construct of selector)266 sampledSelectedValues.add(selected);267 selectorEnabled = selectorEnabled || sampledSelectedValues.size >= requestedMin;268 if (!comparatorEnabled && comparator !== undefined) {269 if (sampledSelectedAndComparedValues.every((p) => !resolvedComparator(p, selected))) {270 // Selected is "different" from all the other known values271 sampledSelectedAndComparedValues.push(selected);272 comparatorEnabled = comparatorEnabled || sampledSelectedAndComparedValues.length >= requestedMin;273 selectorEnabled = selectorEnabled || comparatorEnabled; // comparator enabled unlocks selector too274 }275 }276 }277 return {278 minLength: withMin ? min : undefined,279 maxLength: withMax ? min + gap : undefined,280 selector: selectorEnabled ? selector : undefined,281 comparator: comparatorEnabled ? comparator : undefined,282 };283 });284 const isCorrect = (value: number[], extra: Extra) => {285 if (extra.minLength !== undefined) {286 expect(value.length).toBeGreaterThanOrEqual(extra.minLength);287 }288 if (extra.maxLength !== undefined) {289 expect(value.length).toBeLessThanOrEqual(extra.maxLength);290 }291 for (const v of value) {292 expect(typeof v).toBe('number');293 }294 const resolvedSelector = resolveSelectorFunction(extra.selector);295 const resolvedComparator = resolveComparatorFunction(extra.comparator);296 const alreadySeen: unknown[] = [];297 for (const v of value) {298 const selected = resolvedSelector(v);299 const matchingEntry = alreadySeen.some((e) => resolvedComparator(e, selected));300 expect(matchingEntry).toBe(false);301 alreadySeen.push(selected);302 }303 };304 const integerUpTo10000AndNaNOrMinusZero = new FakeIntegerArbitrary(-2, 10000).map(305 (v) => (v === -2 ? Number.NaN : v === -1 ? -0 : v),306 (v) => {307 if (typeof v !== 'number' || v === -1 || v === -2) throw new Error('');308 return Object.is(v, Number.NaN) ? -2 : Object.is(v, -0) ? -1 : v;309 }310 );311 const uniqueArrayBuilder = (extra: Extra) => uniqueArray(integerUpTo10000AndNaNOrMinusZero, extra);312 it('should produce the same values given the same seed', () => {313 assertProduceSameValueGivenSameSeed(uniqueArrayBuilder, { extraParameters });314 });315 it('should only produce correct values', () => {316 assertProduceCorrectValues(uniqueArrayBuilder, isCorrect, { extraParameters });317 });318 it('should produce values seen as shrinkable without any context', () => {319 assertProduceValuesShrinkableWithoutContext(uniqueArrayBuilder, { extraParameters });320 });321 it('should be able to shrink to the same values without initial context', () => {322 assertShrinkProducesSameValueWithoutInitialContext(uniqueArrayBuilder, { extraParameters });323 });324 // Property: should preserve strictly smaller ordering in shrink325 // Is not applicable in the case of `set` as some values may not be in the "before" version326 // of the array while they can suddenly appear on shrink. They might have been hidden because327 // another value inside the array shadowed them. While on shrink those entries shadowing others328 // may have disappear.329 it.each`330 source | constraints331 ${[2, 4, 8] /* not large enough */} | ${{ minLength: 4 }}332 ${[2, 4, 8] /* too large */} | ${{ maxLength: 2 }}333 ${[2, 4, 8] /* not unique for selector */} | ${{ selector: (item: number) => Math.floor(item / 5) }}334 ${[2, 4, 8] /* not unique for comparator */} | ${{ comparator: (a: number, b: number) => Math.abs(a - b) <= 2 }}335 `('should not be able to generate $source with fc.uniqueArray(arb, $constraints)', ({ source, constraints }) => {336 // Arrange / Act337 const arb = uniqueArray(new FakeIntegerArbitrary(0, 1000), constraints);338 const out = arb.canShrinkWithoutContext(source);339 // Assert340 expect(out).toBe(false);341 });342 it.each`343 rawValue | constraints344 ${[2, 4, 8, 16, 32, 64]} | ${{}}345 ${[2, 4, 8]} | ${{}}346 ${[2, 4, 8]} | ${{ minLength: 2 }}347 ${[2, 4, 8]} | ${{ minLength: 3 }}348 ${[2, 8]} | ${{ selector: (item: number) => Math.floor(item / 5) }}349 ${[2, 8]} | ${{ comparator: (a: number, b: number) => Math.abs(a - b) <= 2 }}350 `('should be able to shrink $rawValue with fc.uniqueArray(arb, $constraints)', ({ rawValue, constraints }) => {351 // Arrange352 const arb = uniqueArray(new FakeIntegerArbitrary(0, 1000), constraints);353 const value = new Value(rawValue, undefined);354 // Act355 const renderedTree = renderTree(buildShrinkTree(arb, value, { numItems: 100 })).join('\n');356 // Assert357 expect(arb.canShrinkWithoutContext(rawValue)).toBe(true);358 expect(renderedTree).toMatchSnapshot();359 });360});361// Helpers362type ComparatorType<T = unknown, U = unknown> = UniqueArrayConstraints<T, U>['comparator'];363function comparatorArbitrary(): fc.Arbitrary<ComparatorType> {364 return fc.oneof(365 fc.constantFrom<ComparatorType>('IsStrictlyEqual', 'SameValue', 'SameValueZero'),366 fc.compareFunc().map((f) => (a: unknown, b: unknown) => f(a, b) === 0)367 );368}369function resolveComparatorFunction<T, U>(370 comparator: ComparatorType<T, U> | undefined371): (a: unknown, b: unknown) => boolean {372 if (comparator === undefined) {373 return (a, b) => Object.is(a, b);374 }375 if (typeof comparator === 'function') {376 return comparator as (a: unknown, b: unknown) => boolean;377 }378 switch (comparator) {379 case 'IsStrictlyEqual':380 return (a, b) => a === b;381 case 'SameValue':382 return (a, b) => Object.is(a, b);383 case 'SameValueZero':384 return (a, b) => (a === 0 && b === 0) || Object.is(a, b);385 }386}387type SelectorType<T = unknown, U = unknown> = UniqueArrayConstraints<T, U>['selector'];388function selectorArbitrary(): fc.Arbitrary<SelectorType> {389 return fc.func(fc.anything());390}391function resolveSelectorFunction<T, U>(selector: SelectorType<T, U> | undefined): (a: unknown) => unknown {392 if (selector === undefined) {393 return (a) => a;394 }395 return selector as (a: unknown) => unknown;...
Using AI Code Generation
1const fc = require("fast-check");2const { integerUpTo10000AndNaNOrMinusZero } = require("fast-check-monorepo");3fc.assert(4 fc.property(integerUpTo10000AndNaNOrMinusZero(), (value) => {5 return value <= 10000;6 })7);8import * as fc from "fast-check";9import { integerUpTo10000AndNaNOrMinusZero } from "fast-check-monorepo";10fc.assert(11 fc.property(integerUpTo10000AndNaNOrMinusZero(), (value) => {12 return value <= 10000;13 })14);15import {16} from "fast-check-monorepo";17import * as fc from "fast-check";18fc.assert(19 fc.property(integerUpTo10000AndNaNOrMinusZero(), (value) => {20 return value <= 10000;21 })22);23import * as fc from "fast-check";24import {25} from "fast-check-monorepo";26fc.assert(27 fc.property(integerUpTo10000AndNaNOrMinusZero(), (value) => {28 return value <= 10000;29 })30);31import {32} from "fast-check-monorepo";33import * as fc from "fast-check";34fc.assert(35 fc.property(integerUpTo10000AndNaNOrMinusZero(), (value) => {36 return value <= 10000;37 })38);39import {40} from "fast-check-monorepo";41import * as fc from "fast-check";42fc.assert(
Using AI Code Generation
1const fc = require('fast-check');2const integerUpTo10000AndNaNOrMinusZero = require('fast-check-monorepo').integerUpTo10000AndNaNOrMinusZero;3const arb = integerUpTo10000AndNaNOrMinusZero();4fc.assert(fc.property(arb, (n) => n >= -0 && n <= 10000 && !isNaN(n)));5 at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)6 at Function.Module._load (internal/modules/cjs/loader.js:725:27)7 at Module.require (internal/modules/cjs/loader.js:952:19)8 at require (internal/modules/cjs/helpers.js:88:18)9 at Object.<anonymous> (/Users/.../test.js:2:47)10 at Module._compile (internal/modules/cjs/loader.js:1063:30)11 at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)12 at Module.load (internal/modules/cjs/loader.js:928:32)13 at Function.Module._load (internal/modules/cjs/loader.js:769:14)14 at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12) {15}16const arb = require('fast-check-monorepo').integerUpTo10000AndNaNOrMinusZero();17const arb = require('fast-check-monorepo').integerUpTo10000AndNaNOrMinusZero();18const arb = require('./node_modules/fast-check-monorepo').integerUpTo10000AndNaNOrMinusZero();19const arb = require('./node_modules/fast-check-monorepo').integerUpTo10000AndNaN
Using AI Code Generation
1const fc = require('fast-check');2const {integerUpTo10000AndNaNOrMinusZero} = require('fast-check-monorepo');3fc.assert(fc.property(fc.integer(), fc.integer(), (a, b) => {4 const result = integerUpTo10000AndNaNOrMinusZero(a, b);5 return result === NaN || result === -0 || (result >= 0 && result <= 10000);6}));7const fc = require('fast-check');8const {integerUpTo10000AndNaNOrMinusZero} = require('fast-check-monorepo');9fc.assert(fc.property(fc.integer(), fc.integer(), (a, b) => {10 const result = integerUpTo10000AndNaNOrMinusZero(a, b);11 return result === NaN || result === -0 || (result >= 0 && result <= 10000);12}));13const fc = require('fast-check');14const {integerUpTo10000AndNaNOrMinusZero} = require('fast-check-monorepo');15fc.assert(fc.property(fc.integer(), fc.integer(), (a, b) => {16 const result = integerUpTo10000AndNaNOrMinusZero(a, b);17 return result === NaN || result === -0 || (result >= 0 && result <= 10000);18}));19const fc = require('fast-check');20const {integerUpTo10000AndNaNOrMinusZero} = require('fast-check-monorepo');21fc.assert(fc.property(fc.integer(), fc.integer(), (a, b) => {22 const result = integerUpTo10000AndNaNOrMinusZero(a, b);23 return result === NaN || result === -0 || (result >= 0 && result <= 10000);24}));25const fc = require('fast-check');26const {integerUpTo10000AndNaNOrMinusZero} = require('fast-check-monorepo');
Using AI Code Generation
1const { integerUpTo10000AndNaNOrMinusZero } = require('fast-check-monorepo');2const property = integerUpTo10000AndNaNOrMinusZero().smap(3 (value) => value.toString(),4 (str) => parseInt(str, 10),5 (value) => value[0]6);7property.checkForall();
Using AI Code Generation
1import fc from 'fast-check';2fc.assert(3 fc.property(fc.integerUpTo10000AndNaNOrMinusZero(), (n) => {4 return true;5 })6);
Using AI Code Generation
1const fc = require('fast-check');2fc.assert(3 fc.property(fc.integerUpTo10000AndNaNOrMinusZero(), (t) => {4 })5);6import * as fc from 'fast-check';7fc.assert(8 fc.property(fc.integerUpTo10000AndNaNOrMinusZero(), (t) => {9 })10);11import * as fc from 'fast-check';12fc.assert(13 fc.property(fc.integerUpTo10000AndNaNOrMinusZero(), (t) => {14 })15);16import * as fc from 'fast-check';17fc.assert(18 fc.property(fc.integerUpTo10000AndNaNOrMinusZero(), (t) => {19 })20);21import * as fc from 'fast-check';22fc.assert(23 fc.property(fc.integerUpTo10000AndNaNOrMinusZero(), (t) => {24 })25);26import * as fc from 'fast-check';27fc.assert(28 fc.property(fc.integerUpTo10000AndNaNOrMinusZero(), (t) => {29 })30);31import * as fc from 'fast-check';32fc.assert(33 fc.property(fc.integerUpTo10000AndNaNOrMinusZero(), (t) => {34 })35);
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!