Best JavaScript code snippet using backstopjs
context_test.js
Source:context_test.js
1const assert = require('assert')2const sinon = require('sinon')3const { Context } = require('automerge/frontend/context')4const { CACHE, OBJECT_ID, CONFLICTS, STATE, ELEM_IDS } = require('automerge/frontend/constants')5const { Counter } = require('automerge/frontend/counter')6const { Table, instantiateTable } = require('automerge/frontend/table')7const { Text } = require('automerge/frontend/text')8const { uuid } = require('automerge')9describe('Proxying context', () => {10 let context, applyPatch11 beforeEach(() => {12 applyPatch = sinon.spy()13 context = new Context({[STATE]: { maxOp: 0 }, [CACHE]: {_root: {}}}, uuid(), applyPatch)14 })15 describe('.setMapKey', () => {16 it('should assign a primitive value to a map key', () => {17 context.setMapKey([], 'sparrows', 5)18 assert(applyPatch.calledOnce)19 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {20 sparrows: {[`1@${context.actorId}`]: {value: 5}}21 }})22 assert.deepStrictEqual(context.ops, [23 {obj: '_root', action: 'set', key: 'sparrows', insert: false, value: 5, pred: []}24 ])25 })26 it('should do nothing if the value was not changed', () => {27 context.cache._root = {[OBJECT_ID]: '_root', goldfinches: 3, [CONFLICTS]: {goldfinches: {'1@actor1': 3}}}28 context.setMapKey([], 'goldfinches', 3)29 assert(applyPatch.notCalled)30 assert.deepStrictEqual(context.ops, [])31 })32 it('should allow a conflict to be resolved', () => {33 context.cache._root = {[OBJECT_ID]: '_root', goldfinches: 5, [CONFLICTS]: {goldfinches: {'1@actor1': 3, '2@actor2': 5}}}34 context.setMapKey([], 'goldfinches', 3)35 assert(applyPatch.calledOnce)36 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {37 goldfinches: {[`1@${context.actorId}`]: {value: 3}}38 }})39 assert.deepStrictEqual(context.ops, [40 {obj: '_root', action: 'set', key: 'goldfinches', insert: false, value: 3, pred: ['1@actor1', '2@actor2']}41 ])42 })43 it('should create nested maps', () => {44 context.setMapKey([], 'birds', {goldfinches: 3})45 assert(applyPatch.calledOnce)46 const objectId = applyPatch.firstCall.args[0].props.birds[`1@${context.actorId}`].objectId47 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {48 birds: {[`1@${context.actorId}`]: {objectId, type: 'map', props: {49 goldfinches: {[`2@${context.actorId}`]: {value: 3}}50 }}}51 }})52 assert.deepStrictEqual(context.ops, [53 {obj: '_root', action: 'makeMap', key: 'birds', insert: false, pred: []},54 {obj: objectId, action: 'set', key: 'goldfinches', insert: false, value: 3, pred: []}55 ])56 })57 it('should perform assignment inside nested maps', () => {58 const objectId = uuid(), child = {[OBJECT_ID]: objectId}59 context.cache[objectId] = child60 context.cache._root = {[OBJECT_ID]: '_root', [CONFLICTS]: {birds: {'1@actor1': child}}, birds: child}61 context.setMapKey([{key: 'birds', objectId}], 'goldfinches', 3)62 assert(applyPatch.calledOnce)63 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {64 birds: {'1@actor1': {objectId, type: 'map', props: {65 goldfinches: {[`1@${context.actorId}`]: {value: 3}}66 }}}67 }})68 assert.deepStrictEqual(context.ops, [69 {obj: objectId, action: 'set', key: 'goldfinches', insert: false, value: 3, pred: []}70 ])71 })72 it('should perform assignment inside conflicted maps', () => {73 const objectId1 = uuid(), child1 = {[OBJECT_ID]: objectId1}74 const objectId2 = uuid(), child2 = {[OBJECT_ID]: objectId2}75 context.cache[objectId1] = child176 context.cache[objectId2] = child277 context.cache._root = {[OBJECT_ID]: '_root', birds: child2,78 [CONFLICTS]: {birds: {'1@actor1': child1, '1@actor2': child2}}}79 context.setMapKey([{key: 'birds', objectId: objectId2}], 'goldfinches', 3)80 assert(applyPatch.calledOnce)81 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {birds: {82 '1@actor1': {objectId: objectId1, type: 'map'},83 '1@actor2': {objectId: objectId2, type: 'map', props: {84 goldfinches: {[`1@${context.actorId}`]: {value: 3}}85 }}86 }}})87 assert.deepStrictEqual(context.ops, [88 {obj: objectId2, action: 'set', key: 'goldfinches', insert: false, value: 3, pred: []}89 ])90 })91 it('should handle conflict values of various types', () => {92 const objectId = uuid(), child = {[OBJECT_ID]: objectId}, dateValue = new Date()93 context.cache[objectId] = child94 context.cache._root = {[OBJECT_ID]: '_root', values: child, [CONFLICTS]: {values: {95 '1@actor1': dateValue, '1@actor2': new Counter(), '1@actor3': 42, '1@actor4': null, '1@actor5': child96 }}}97 context.setMapKey([{key: 'values', objectId}], 'goldfinches', 3)98 assert(applyPatch.calledOnce)99 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {values: {100 '1@actor1': {value: dateValue.getTime(), datatype: 'timestamp'},101 '1@actor2': {value: 0, datatype: 'counter'},102 '1@actor3': {value: 42},103 '1@actor4': {value: null},104 '1@actor5': {objectId, type: 'map', props: {goldfinches: {[`1@${context.actorId}`]: {value: 3}}}}105 }}})106 assert.deepStrictEqual(context.ops, [107 {obj: objectId, action: 'set', key: 'goldfinches', insert: false, value: 3, pred: []}108 ])109 })110 it('should create nested lists', () => {111 context.setMapKey([], 'birds', ['sparrow', 'goldfinch'])112 assert(applyPatch.calledOnce)113 const objectId = applyPatch.firstCall.args[0].props.birds[`1@${context.actorId}`].objectId114 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {115 birds: {[`1@${context.actorId}`]: {objectId, type: 'list', props: {116 0: {[`2@${context.actorId}`]: {value: 'sparrow'}},117 1: {[`3@${context.actorId}`]: {value: 'goldfinch'}}118 }, edits: [119 {action: 'insert', index: 0, elemId: `2@${context.actorId}`},120 {action: 'insert', index: 1, elemId: `3@${context.actorId}`}121 ]}}122 }})123 assert.deepStrictEqual(context.ops, [124 {obj: '_root', action: 'makeList', key: 'birds', insert: false, pred: []},125 {obj: objectId, action: 'set', elemId: '_head', insert: true, value: 'sparrow', pred: []},126 {obj: objectId, action: 'set', elemId: `2@${context.actorId}`, insert: true, value: 'goldfinch', pred: []}127 ])128 })129 it('should create nested Text objects', () => {130 context.setMapKey([], 'text', new Text('hi'))131 const objectId = applyPatch.firstCall.args[0].props.text[`1@${context.actorId}`].objectId132 assert(applyPatch.calledOnce)133 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {134 text: {[`1@${context.actorId}`]: {objectId, type: 'text', props: {135 0: {[`2@${context.actorId}`]: {value: 'h'}},136 1: {[`3@${context.actorId}`]: {value: 'i'}}137 }, edits: [138 {action: 'insert', index: 0, elemId: `2@${context.actorId}`},139 {action: 'insert', index: 1, elemId: `3@${context.actorId}`}140 ]}}141 }})142 assert.deepStrictEqual(context.ops, [143 {obj: '_root', action: 'makeText', key: 'text', insert: false, pred: []},144 {obj: objectId, action: 'set', elemId: '_head', insert: true, value: 'h', pred: []},145 {obj: objectId, action: 'set', elemId: `2@${context.actorId}`, insert: true, value: 'i', pred: []}146 ])147 })148 it('should create nested Table objects', () => {149 context.setMapKey([], 'books', new Table())150 assert(applyPatch.calledOnce)151 const objectId = applyPatch.firstCall.args[0].props.books[`1@${context.actorId}`].objectId152 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {153 books: {[`1@${context.actorId}`]: {objectId, type: 'table', props: {}}}154 }})155 assert.deepStrictEqual(context.ops, [156 {obj: '_root', action: 'makeTable', key: 'books', insert: false, pred: []}157 ])158 })159 it('should allow assignment of Date values', () => {160 const now = new Date()161 context.setMapKey([], 'now', now)162 assert(applyPatch.calledOnce)163 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {164 now: {[`1@${context.actorId}`]: {value: now.getTime(), datatype: 'timestamp'}}165 }})166 assert.deepStrictEqual(context.ops, [167 {obj: '_root', action: 'set', key: 'now', insert: false, value: now.getTime(), datatype: 'timestamp', pred: []}168 ])169 })170 it('should allow assignment of Counter values', () => {171 const counter = new Counter(3)172 context.setMapKey([], 'counter', counter)173 assert(applyPatch.calledOnce)174 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {175 counter: {[`1@${context.actorId}`]: {value: 3, datatype: 'counter'}}176 }})177 assert.deepStrictEqual(context.ops, [178 {obj: '_root', action: 'set', key: 'counter', insert: false, value: 3, datatype: 'counter', pred: []}179 ])180 })181 })182 describe('.deleteMapKey', () => {183 it('should remove an existing key', () => {184 context.cache._root = {[OBJECT_ID]: '_root', goldfinches: 3, [CONFLICTS]: {goldfinches: {'1@actor1': 3}}}185 context.deleteMapKey([], 'goldfinches')186 assert(applyPatch.calledOnce)187 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {goldfinches: {}}})188 assert.deepStrictEqual(context.ops, [189 {obj: '_root', action: 'del', key: 'goldfinches', insert: false, pred: ['1@actor1']}190 ])191 })192 it('should do nothing if the key does not exist', () => {193 context.cache._root = {[OBJECT_ID]: '_root', goldfinches: 3, [CONFLICTS]: {goldfinches: {'1@actor1': 3}}}194 context.deleteMapKey([], 'sparrows')195 const expected = {objectId: '_root', type: 'map'}196 assert(applyPatch.notCalled)197 assert.deepStrictEqual(context.ops, [])198 })199 it('should update a nested object', () => {200 const objectId = uuid(), child = {[OBJECT_ID]: objectId, [CONFLICTS]: {goldfinches: {'5@actor1': 3}}, goldfinches: 3}201 context.cache[objectId] = child202 context.cache._root = {[OBJECT_ID]: '_root', [CONFLICTS]: {birds: {'1@actor1': child}}, birds: child}203 context.deleteMapKey([{key: 'birds', objectId}], 'goldfinches')204 assert(applyPatch.calledOnce)205 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {206 birds: {'1@actor1': {objectId, type: 'map', props: {goldfinches: {}}}}207 }})208 assert.deepStrictEqual(context.ops, [209 {obj: objectId, action: 'del', key: 'goldfinches', insert: false, pred: ['5@actor1']}210 ])211 })212 })213 describe('list manipulation', () => {214 let listId, list215 beforeEach(() => {216 listId = uuid()217 list = ['swallow', 'magpie']218 Object.defineProperty(list, OBJECT_ID, {value: listId})219 Object.defineProperty(list, CONFLICTS, {value: [{'1@xxx': 'swallow'}, {'2@xxx': 'magpie'}]})220 Object.defineProperty(list, ELEM_IDS, {value: ['1@xxx', '2@xxx']})221 context.cache[listId] = list222 context.cache._root = {[OBJECT_ID]: '_root', birds: list, [CONFLICTS]: {birds: {'1@actor1': list}}}223 })224 it('should overwrite an existing list element', () => {225 context.setListIndex([{key: 'birds', objectId: listId}], 0, 'starling')226 assert(applyPatch.calledOnce)227 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {228 birds: {'1@actor1': {objectId: listId, type: 'list', props: {229 0: {[`1@${context.actorId}`]: {value: 'starling'}}230 }}}231 }})232 assert.deepStrictEqual(context.ops, [233 {obj: listId, action: 'set', elemId: '1@xxx', insert: false, value: 'starling', pred: ['1@xxx']}234 ])235 })236 it('should create nested objects on assignment', () => {237 context.setListIndex([{key: 'birds', objectId: listId}], 1, {english: 'goldfinch', latin: 'carduelis'})238 assert(applyPatch.calledOnce)239 const nestedId = applyPatch.firstCall.args[0].props.birds['1@actor1'].props[1][`1@${context.actorId}`].objectId240 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {241 birds: {'1@actor1': {objectId: listId, type: 'list', props: {242 1: {[`1@${context.actorId}`]: {objectId: nestedId, type: 'map', props: {243 english: {[`2@${context.actorId}`]: {value: 'goldfinch'}},244 latin: {[`3@${context.actorId}`]: {value: 'carduelis'}}245 }}}246 }}}247 }})248 assert.deepStrictEqual(context.ops, [249 {obj: listId, action: 'makeMap', elemId: '2@xxx', insert: false, pred: ['2@xxx']},250 {obj: nestedId, action: 'set', key: 'english', insert: false, value: 'goldfinch', pred: []},251 {obj: nestedId, action: 'set', key: 'latin', insert: false, value: 'carduelis', pred: []}252 ])253 })254 it('should create nested objects on insertion', () => {255 context.splice([{key: 'birds', objectId: listId}], 2, 0, [{english: 'goldfinch', latin: 'carduelis'}])256 assert(applyPatch.calledOnce)257 const nestedId = applyPatch.firstCall.args[0].props.birds['1@actor1'].props[2][`1@${context.actorId}`].objectId258 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {259 birds: {'1@actor1': {objectId: listId, type: 'list', edits: [260 {action: 'insert', index: 2, elemId: `1@${context.actorId}`}261 ], props: {262 2: {[`1@${context.actorId}`]: {objectId: nestedId, type: 'map', props: {263 english: {[`2@${context.actorId}`]: {value: 'goldfinch'}},264 latin: {[`3@${context.actorId}`]: {value: 'carduelis'}}265 }}}266 }}}267 }})268 assert.deepStrictEqual(context.ops, [269 {obj: listId, action: 'makeMap', elemId: '2@xxx', insert: true, pred: []},270 {obj: nestedId, action: 'set', key: 'english', insert: false, value: 'goldfinch', pred: []},271 {obj: nestedId, action: 'set', key: 'latin', insert: false, value: 'carduelis', pred: []}272 ])273 })274 it('should support deleting list elements', () => {275 context.splice([{key: 'birds', objectId: listId}], 0, 2, [])276 assert(applyPatch.calledOnce)277 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {278 birds: {'1@actor1': {objectId: listId, type: 'list', props: {}, edits: [279 {action: 'remove', index: 0}, {action: 'remove', index: 0}280 ]}}281 }})282 assert.deepStrictEqual(context.ops, [283 {obj: listId, action: 'del', elemId: '1@xxx', insert: false, pred: ['1@xxx']},284 {obj: listId, action: 'del', elemId: '2@xxx', insert: false, pred: ['2@xxx']}285 ])286 })287 it('should support list splicing', () => {288 context.splice([{key: 'birds', objectId: listId}], 0, 1, ['starling', 'goldfinch'])289 assert(applyPatch.calledOnce)290 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {291 birds: {'1@actor1': {objectId: listId, type: 'list', edits: [292 {action: 'remove', index: 0},293 {action: 'insert', index: 0, elemId: `2@${context.actorId}`},294 {action: 'insert', index: 1, elemId: `3@${context.actorId}`}295 ], props: {296 0: {[`2@${context.actorId}`]: {value: 'starling'}},297 1: {[`3@${context.actorId}`]: {value: 'goldfinch'}}298 }}}299 }})300 assert.deepStrictEqual(context.ops, [301 {obj: listId, action: 'del', elemId: '1@xxx', insert: false, pred: ['1@xxx']},302 {obj: listId, action: 'set', elemId: '_head', insert: true, value: 'starling', pred: []},303 {obj: listId, action: 'set', elemId: `2@${context.actorId}`, insert: true, value: 'goldfinch', pred: []}304 ])305 })306 })307 describe('Table manipulation', () => {308 let tableId, table309 beforeEach(() => {310 tableId = uuid()311 table = instantiateTable(tableId)312 context.cache[tableId] = table313 context.cache._root = {[OBJECT_ID]: '_root', books: table, [CONFLICTS]: {books: {'1@actor1': table}}}314 })315 it('should add a table row', () => {316 const rowId = context.addTableRow([{key: 'books', objectId: tableId}], {author: 'Mary Shelley', title: 'Frankenstein'})317 assert(applyPatch.calledOnce)318 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {319 books: {'1@actor1': {objectId: tableId, type: 'table', props: {320 [rowId]: {[`1@${context.actorId}`]: {objectId: `1@${context.actorId}`, type: 'map', props: {321 author: {[`2@${context.actorId}`]: {value: 'Mary Shelley'}},322 title: {[`3@${context.actorId}`]: {value: 'Frankenstein'}}323 }}}324 }}}325 }})326 assert.deepStrictEqual(context.ops, [327 {obj: tableId, action: 'makeMap', key: rowId, insert: false, pred: []},328 {obj: `1@${context.actorId}`, action: 'set', key: 'author', insert: false, value: 'Mary Shelley', pred: []},329 {obj: `1@${context.actorId}`, action: 'set', key: 'title', insert: false, value: 'Frankenstein', pred: []}330 ])331 })332 it('should delete a table row', () => {333 const rowId = uuid()334 const row = {author: 'Mary Shelley', title: 'Frankenstein'}335 row[OBJECT_ID] = rowId336 table.entries[rowId] = row337 context.deleteTableRow([{key: 'books', objectId: tableId}], rowId, '5@actor1')338 assert(applyPatch.calledOnce)339 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {340 books: {'1@actor1': {objectId: tableId, type: 'table', props: {[rowId]: {}}}}341 }})342 assert.deepStrictEqual(context.ops, [343 {obj: tableId, action: 'del', key: rowId, insert: false, pred: ['5@actor1']}344 ])345 })346 })347 it('should increment a counter', () => {348 const counter = new Counter()349 context.cache._root = {[OBJECT_ID]: '_root', counter, [CONFLICTS]: {counter: {'1@actor1': counter}}}350 context.increment([], 'counter', 1)351 assert(applyPatch.calledOnce)352 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {353 counter: {[`1@${context.actorId}`]: {value: 1, datatype: 'counter'}}354 }})355 assert.deepStrictEqual(context.ops, [{obj: '_root', action: 'inc', key: 'counter', insert: false, value: 1, pred: ['1@actor1']}])356 })...
context_test.mjs
Source:context_test.mjs
1import assert from 'assert'2import sinon from 'sinon'3import { Context } from 'automerge/frontend/context.js'4import { CACHE, OBJECT_ID, CONFLICTS, STATE, ELEM_IDS } from 'automerge/frontend/constants.js'5import { Counter } from 'automerge/frontend/counter.js'6import { Table, instantiateTable } from 'automerge/frontend/table.js'7import { Text } from 'automerge/frontend/text.js'8import { uuid } from 'automerge'9describe('Proxying context', () => {10 let context, applyPatch11 beforeEach(() => {12 applyPatch = sinon.spy()13 context = new Context({[STATE]: { maxOp: 0 }, [CACHE]: {_root: {}}}, uuid(), applyPatch)14 })15 describe('.setMapKey', () => {16 it('should assign a primitive value to a map key', () => {17 context.setMapKey([], 'sparrows', 5)18 assert(applyPatch.calledOnce)19 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {20 sparrows: {[`1@${context.actorId}`]: {value: 5}}21 }})22 assert.deepStrictEqual(context.ops, [23 {obj: '_root', action: 'set', key: 'sparrows', insert: false, value: 5, pred: []}24 ])25 })26 it('should do nothing if the value was not changed', () => {27 context.cache._root = {[OBJECT_ID]: '_root', goldfinches: 3, [CONFLICTS]: {goldfinches: {'1@actor1': 3}}}28 context.setMapKey([], 'goldfinches', 3)29 assert(applyPatch.notCalled)30 assert.deepStrictEqual(context.ops, [])31 })32 it('should allow a conflict to be resolved', () => {33 context.cache._root = {[OBJECT_ID]: '_root', goldfinches: 5, [CONFLICTS]: {goldfinches: {'1@actor1': 3, '2@actor2': 5}}}34 context.setMapKey([], 'goldfinches', 3)35 assert(applyPatch.calledOnce)36 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {37 goldfinches: {[`1@${context.actorId}`]: {value: 3}}38 }})39 assert.deepStrictEqual(context.ops, [40 {obj: '_root', action: 'set', key: 'goldfinches', insert: false, value: 3, pred: ['1@actor1', '2@actor2']}41 ])42 })43 it('should create nested maps', () => {44 context.setMapKey([], 'birds', {goldfinches: 3})45 assert(applyPatch.calledOnce)46 const objectId = applyPatch.firstCall.args[0].props.birds[`1@${context.actorId}`].objectId47 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {48 birds: {[`1@${context.actorId}`]: {objectId, type: 'map', props: {49 goldfinches: {[`2@${context.actorId}`]: {value: 3}}50 }}}51 }})52 assert.deepStrictEqual(context.ops, [53 {obj: '_root', action: 'makeMap', key: 'birds', insert: false, pred: []},54 {obj: objectId, action: 'set', key: 'goldfinches', insert: false, value: 3, pred: []}55 ])56 })57 it('should perform assignment inside nested maps', () => {58 const objectId = uuid(), child = {[OBJECT_ID]: objectId}59 context.cache[objectId] = child60 context.cache._root = {[OBJECT_ID]: '_root', [CONFLICTS]: {birds: {'1@actor1': child}}, birds: child}61 context.setMapKey([{key: 'birds', objectId}], 'goldfinches', 3)62 assert(applyPatch.calledOnce)63 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {64 birds: {'1@actor1': {objectId, type: 'map', props: {65 goldfinches: {[`1@${context.actorId}`]: {value: 3}}66 }}}67 }})68 assert.deepStrictEqual(context.ops, [69 {obj: objectId, action: 'set', key: 'goldfinches', insert: false, value: 3, pred: []}70 ])71 })72 it('should perform assignment inside conflicted maps', () => {73 const objectId1 = uuid(), child1 = {[OBJECT_ID]: objectId1}74 const objectId2 = uuid(), child2 = {[OBJECT_ID]: objectId2}75 context.cache[objectId1] = child176 context.cache[objectId2] = child277 context.cache._root = {[OBJECT_ID]: '_root', birds: child2,78 [CONFLICTS]: {birds: {'1@actor1': child1, '1@actor2': child2}}}79 context.setMapKey([{key: 'birds', objectId: objectId2}], 'goldfinches', 3)80 assert(applyPatch.calledOnce)81 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {birds: {82 '1@actor1': {objectId: objectId1, type: 'map'},83 '1@actor2': {objectId: objectId2, type: 'map', props: {84 goldfinches: {[`1@${context.actorId}`]: {value: 3}}85 }}86 }}})87 assert.deepStrictEqual(context.ops, [88 {obj: objectId2, action: 'set', key: 'goldfinches', insert: false, value: 3, pred: []}89 ])90 })91 it('should handle conflict values of various types', () => {92 const objectId = uuid(), child = {[OBJECT_ID]: objectId}, dateValue = new Date()93 context.cache[objectId] = child94 context.cache._root = {[OBJECT_ID]: '_root', values: child, [CONFLICTS]: {values: {95 '1@actor1': dateValue, '1@actor2': new Counter(), '1@actor3': 42, '1@actor4': null, '1@actor5': child96 }}}97 context.setMapKey([{key: 'values', objectId}], 'goldfinches', 3)98 assert(applyPatch.calledOnce)99 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {values: {100 '1@actor1': {value: dateValue.getTime(), datatype: 'timestamp'},101 '1@actor2': {value: 0, datatype: 'counter'},102 '1@actor3': {value: 42},103 '1@actor4': {value: null},104 '1@actor5': {objectId, type: 'map', props: {goldfinches: {[`1@${context.actorId}`]: {value: 3}}}}105 }}})106 assert.deepStrictEqual(context.ops, [107 {obj: objectId, action: 'set', key: 'goldfinches', insert: false, value: 3, pred: []}108 ])109 })110 it('should create nested lists', () => {111 context.setMapKey([], 'birds', ['sparrow', 'goldfinch'])112 assert(applyPatch.calledOnce)113 const objectId = applyPatch.firstCall.args[0].props.birds[`1@${context.actorId}`].objectId114 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {115 birds: {[`1@${context.actorId}`]: {objectId, type: 'list', props: {116 0: {[`2@${context.actorId}`]: {value: 'sparrow'}},117 1: {[`3@${context.actorId}`]: {value: 'goldfinch'}}118 }, edits: [119 {action: 'insert', index: 0, elemId: `2@${context.actorId}`},120 {action: 'insert', index: 1, elemId: `3@${context.actorId}`}121 ]}}122 }})123 assert.deepStrictEqual(context.ops, [124 {obj: '_root', action: 'makeList', key: 'birds', insert: false, pred: []},125 {obj: objectId, action: 'set', elemId: '_head', insert: true, value: 'sparrow', pred: []},126 {obj: objectId, action: 'set', elemId: `2@${context.actorId}`, insert: true, value: 'goldfinch', pred: []}127 ])128 })129 it('should create nested Text objects', () => {130 context.setMapKey([], 'text', new Text('hi'))131 const objectId = applyPatch.firstCall.args[0].props.text[`1@${context.actorId}`].objectId132 assert(applyPatch.calledOnce)133 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {134 text: {[`1@${context.actorId}`]: {objectId, type: 'text', props: {135 0: {[`2@${context.actorId}`]: {value: 'h'}},136 1: {[`3@${context.actorId}`]: {value: 'i'}}137 }, edits: [138 {action: 'insert', index: 0, elemId: `2@${context.actorId}`},139 {action: 'insert', index: 1, elemId: `3@${context.actorId}`}140 ]}}141 }})142 assert.deepStrictEqual(context.ops, [143 {obj: '_root', action: 'makeText', key: 'text', insert: false, pred: []},144 {obj: objectId, action: 'set', elemId: '_head', insert: true, value: 'h', pred: []},145 {obj: objectId, action: 'set', elemId: `2@${context.actorId}`, insert: true, value: 'i', pred: []}146 ])147 })148 it('should create nested Table objects', () => {149 context.setMapKey([], 'books', new Table())150 assert(applyPatch.calledOnce)151 const objectId = applyPatch.firstCall.args[0].props.books[`1@${context.actorId}`].objectId152 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {153 books: {[`1@${context.actorId}`]: {objectId, type: 'table', props: {}}}154 }})155 assert.deepStrictEqual(context.ops, [156 {obj: '_root', action: 'makeTable', key: 'books', insert: false, pred: []}157 ])158 })159 it('should allow assignment of Date values', () => {160 const now = new Date()161 context.setMapKey([], 'now', now)162 assert(applyPatch.calledOnce)163 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {164 now: {[`1@${context.actorId}`]: {value: now.getTime(), datatype: 'timestamp'}}165 }})166 assert.deepStrictEqual(context.ops, [167 {obj: '_root', action: 'set', key: 'now', insert: false, value: now.getTime(), datatype: 'timestamp', pred: []}168 ])169 })170 it('should allow assignment of Counter values', () => {171 const counter = new Counter(3)172 context.setMapKey([], 'counter', counter)173 assert(applyPatch.calledOnce)174 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {175 counter: {[`1@${context.actorId}`]: {value: 3, datatype: 'counter'}}176 }})177 assert.deepStrictEqual(context.ops, [178 {obj: '_root', action: 'set', key: 'counter', insert: false, value: 3, datatype: 'counter', pred: []}179 ])180 })181 })182 describe('.deleteMapKey', () => {183 it('should remove an existing key', () => {184 context.cache._root = {[OBJECT_ID]: '_root', goldfinches: 3, [CONFLICTS]: {goldfinches: {'1@actor1': 3}}}185 context.deleteMapKey([], 'goldfinches')186 assert(applyPatch.calledOnce)187 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {goldfinches: {}}})188 assert.deepStrictEqual(context.ops, [189 {obj: '_root', action: 'del', key: 'goldfinches', insert: false, pred: ['1@actor1']}190 ])191 })192 it('should do nothing if the key does not exist', () => {193 context.cache._root = {[OBJECT_ID]: '_root', goldfinches: 3, [CONFLICTS]: {goldfinches: {'1@actor1': 3}}}194 context.deleteMapKey([], 'sparrows')195 const expected = {objectId: '_root', type: 'map'}196 assert(applyPatch.notCalled)197 assert.deepStrictEqual(context.ops, [])198 })199 it('should update a nested object', () => {200 const objectId = uuid(), child = {[OBJECT_ID]: objectId, [CONFLICTS]: {goldfinches: {'5@actor1': 3}}, goldfinches: 3}201 context.cache[objectId] = child202 context.cache._root = {[OBJECT_ID]: '_root', [CONFLICTS]: {birds: {'1@actor1': child}}, birds: child}203 context.deleteMapKey([{key: 'birds', objectId}], 'goldfinches')204 assert(applyPatch.calledOnce)205 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {206 birds: {'1@actor1': {objectId, type: 'map', props: {goldfinches: {}}}}207 }})208 assert.deepStrictEqual(context.ops, [209 {obj: objectId, action: 'del', key: 'goldfinches', insert: false, pred: ['5@actor1']}210 ])211 })212 })213 describe('list manipulation', () => {214 let listId, list215 beforeEach(() => {216 listId = uuid()217 list = ['swallow', 'magpie']218 Object.defineProperty(list, OBJECT_ID, {value: listId})219 Object.defineProperty(list, CONFLICTS, {value: [{'1@xxx': 'swallow'}, {'2@xxx': 'magpie'}]})220 Object.defineProperty(list, ELEM_IDS, {value: ['1@xxx', '2@xxx']})221 context.cache[listId] = list222 context.cache._root = {[OBJECT_ID]: '_root', birds: list, [CONFLICTS]: {birds: {'1@actor1': list}}}223 })224 it('should overwrite an existing list element', () => {225 context.setListIndex([{key: 'birds', objectId: listId}], 0, 'starling')226 assert(applyPatch.calledOnce)227 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {228 birds: {'1@actor1': {objectId: listId, type: 'list', props: {229 0: {[`1@${context.actorId}`]: {value: 'starling'}}230 }}}231 }})232 assert.deepStrictEqual(context.ops, [233 {obj: listId, action: 'set', elemId: '1@xxx', insert: false, value: 'starling', pred: ['1@xxx']}234 ])235 })236 it('should create nested objects on assignment', () => {237 context.setListIndex([{key: 'birds', objectId: listId}], 1, {english: 'goldfinch', latin: 'carduelis'})238 assert(applyPatch.calledOnce)239 const nestedId = applyPatch.firstCall.args[0].props.birds['1@actor1'].props[1][`1@${context.actorId}`].objectId240 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {241 birds: {'1@actor1': {objectId: listId, type: 'list', props: {242 1: {[`1@${context.actorId}`]: {objectId: nestedId, type: 'map', props: {243 english: {[`2@${context.actorId}`]: {value: 'goldfinch'}},244 latin: {[`3@${context.actorId}`]: {value: 'carduelis'}}245 }}}246 }}}247 }})248 assert.deepStrictEqual(context.ops, [249 {obj: listId, action: 'makeMap', elemId: '2@xxx', insert: false, pred: ['2@xxx']},250 {obj: nestedId, action: 'set', key: 'english', insert: false, value: 'goldfinch', pred: []},251 {obj: nestedId, action: 'set', key: 'latin', insert: false, value: 'carduelis', pred: []}252 ])253 })254 it('should create nested objects on insertion', () => {255 context.splice([{key: 'birds', objectId: listId}], 2, 0, [{english: 'goldfinch', latin: 'carduelis'}])256 assert(applyPatch.calledOnce)257 const nestedId = applyPatch.firstCall.args[0].props.birds['1@actor1'].props[2][`1@${context.actorId}`].objectId258 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {259 birds: {'1@actor1': {objectId: listId, type: 'list', edits: [260 {action: 'insert', index: 2, elemId: `1@${context.actorId}`}261 ], props: {262 2: {[`1@${context.actorId}`]: {objectId: nestedId, type: 'map', props: {263 english: {[`2@${context.actorId}`]: {value: 'goldfinch'}},264 latin: {[`3@${context.actorId}`]: {value: 'carduelis'}}265 }}}266 }}}267 }})268 assert.deepStrictEqual(context.ops, [269 {obj: listId, action: 'makeMap', elemId: '2@xxx', insert: true, pred: []},270 {obj: nestedId, action: 'set', key: 'english', insert: false, value: 'goldfinch', pred: []},271 {obj: nestedId, action: 'set', key: 'latin', insert: false, value: 'carduelis', pred: []}272 ])273 })274 it('should support deleting list elements', () => {275 context.splice([{key: 'birds', objectId: listId}], 0, 2, [])276 assert(applyPatch.calledOnce)277 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {278 birds: {'1@actor1': {objectId: listId, type: 'list', props: {}, edits: [279 {action: 'remove', index: 0}, {action: 'remove', index: 0}280 ]}}281 }})282 assert.deepStrictEqual(context.ops, [283 {obj: listId, action: 'del', elemId: '1@xxx', insert: false, pred: ['1@xxx']},284 {obj: listId, action: 'del', elemId: '2@xxx', insert: false, pred: ['2@xxx']}285 ])286 })287 it('should support list splicing', () => {288 context.splice([{key: 'birds', objectId: listId}], 0, 1, ['starling', 'goldfinch'])289 assert(applyPatch.calledOnce)290 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {291 birds: {'1@actor1': {objectId: listId, type: 'list', edits: [292 {action: 'remove', index: 0},293 {action: 'insert', index: 0, elemId: `2@${context.actorId}`},294 {action: 'insert', index: 1, elemId: `3@${context.actorId}`}295 ], props: {296 0: {[`2@${context.actorId}`]: {value: 'starling'}},297 1: {[`3@${context.actorId}`]: {value: 'goldfinch'}}298 }}}299 }})300 assert.deepStrictEqual(context.ops, [301 {obj: listId, action: 'del', elemId: '1@xxx', insert: false, pred: ['1@xxx']},302 {obj: listId, action: 'set', elemId: '_head', insert: true, value: 'starling', pred: []},303 {obj: listId, action: 'set', elemId: `2@${context.actorId}`, insert: true, value: 'goldfinch', pred: []}304 ])305 })306 })307 describe('Table manipulation', () => {308 let tableId, table309 beforeEach(() => {310 tableId = uuid()311 table = instantiateTable(tableId)312 context.cache[tableId] = table313 context.cache._root = {[OBJECT_ID]: '_root', books: table, [CONFLICTS]: {books: {'1@actor1': table}}}314 })315 it('should add a table row', () => {316 const rowId = context.addTableRow([{key: 'books', objectId: tableId}], {author: 'Mary Shelley', title: 'Frankenstein'})317 assert(applyPatch.calledOnce)318 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {319 books: {'1@actor1': {objectId: tableId, type: 'table', props: {320 [rowId]: {[`1@${context.actorId}`]: {objectId: `1@${context.actorId}`, type: 'map', props: {321 author: {[`2@${context.actorId}`]: {value: 'Mary Shelley'}},322 title: {[`3@${context.actorId}`]: {value: 'Frankenstein'}}323 }}}324 }}}325 }})326 assert.deepStrictEqual(context.ops, [327 {obj: tableId, action: 'makeMap', key: rowId, insert: false, pred: []},328 {obj: `1@${context.actorId}`, action: 'set', key: 'author', insert: false, value: 'Mary Shelley', pred: []},329 {obj: `1@${context.actorId}`, action: 'set', key: 'title', insert: false, value: 'Frankenstein', pred: []}330 ])331 })332 it('should delete a table row', () => {333 const rowId = uuid()334 const row = {author: 'Mary Shelley', title: 'Frankenstein'}335 row[OBJECT_ID] = rowId336 table.entries[rowId] = row337 context.deleteTableRow([{key: 'books', objectId: tableId}], rowId, '5@actor1')338 assert(applyPatch.calledOnce)339 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {340 books: {'1@actor1': {objectId: tableId, type: 'table', props: {[rowId]: {}}}}341 }})342 assert.deepStrictEqual(context.ops, [343 {obj: tableId, action: 'del', key: rowId, insert: false, pred: ['5@actor1']}344 ])345 })346 })347 it('should increment a counter', () => {348 const counter = new Counter()349 context.cache._root = {[OBJECT_ID]: '_root', counter, [CONFLICTS]: {counter: {'1@actor1': counter}}}350 context.increment([], 'counter', 1)351 assert(applyPatch.calledOnce)352 assert.deepStrictEqual(applyPatch.firstCall.args[0], {objectId: '_root', type: 'map', props: {353 counter: {[`1@${context.actorId}`]: {value: 1, datatype: 'counter'}}354 }})355 assert.deepStrictEqual(context.ops, [{obj: '_root', action: 'inc', key: 'counter', insert: false, value: 1, pred: ['1@actor1']}])356 })...
apply-patch.spec.ts
Source:apply-patch.spec.ts
...25 expect(await queue).toBe(name);26 assertExecutedCommands(...executedCommands);27 });28 it.each(applyPatchTests)(`(legacy) promise usage - %s %s`, async (_api, name, applyPatchArgs, executedCommands) => {29 const queue = newSimpleGitP().applyPatch(...applyPatchArgs);30 await closeWithSuccess(name);31 expect(await queue).toBe(name);32 assertExecutedCommands(...executedCommands);33 });34 });35 describe('usage', () => {36 let callback: jest.Mock;37 const tests: Array<[string, RegExp | null, 'Y' | 'N', (git: SimpleGit) => Promise<string>]> = [38 ['patch - no-opt - no-callback ', null, 'N', (git) => git.applyPatch('foo')],39 ['patch - array-opt - no-callback ', null, 'N', (git) => git.applyPatch('foo', ['--opt'])],40 ['patch - object-opt - no-callback ', null, 'N', (git) => git.applyPatch('foo', {'--opt': null})],41 ['patch - no-opt - with-callback', null, 'Y', (git) => git.applyPatch('foo', callback)],42 ['patch - array-opt - with-callback', null, 'Y', (git) => git.applyPatch('foo', ['--opt'], callback)],43 ['patch - object-opt - with-callback', null, 'Y', (git) => git.applyPatch('foo', {'--opt': null}, callback)],44 ['patches - no-opt - no-callback ', null, 'N', (git) => git.applyPatch(['foo', 'bar'])],45 ['patches - array-opt - no-callback ', null, 'N', (git) => git.applyPatch(['foo', 'bar'], ['--opt'])],46 ['patches - object-opt - no-callback ', null, 'N', (git) => git.applyPatch(['foo', 'bar'], {'--opt': null})],47 ['patches - no-opt - with-callback', null, 'Y', (git) => git.applyPatch(['foo', 'bar'], callback)],48 ['patches - array-opt - with-callback', null, 'Y', (git) => git.applyPatch(['foo', 'bar'], ['--opt'], callback)],49 ['patches - object-opt - with-callback', null, 'Y', (git) => git.applyPatch(['foo', 'bar'], {'--opt': null}, callback)],50 ['error: no patches', /string patches/, 'N', (git) => git.applyPatch({'--opt': null} as any)],51 ];52 const noCallbackTests = tests.filter(t => t[2] === 'N');53 beforeEach(() => callback = jest.fn());54 it.each(tests)(`git.applyPatch %s`, async (name, error, withCallback, task) => {55 const result = task(newSimpleGit());56 if (error) {57 return assertGitError(await promiseError(result), error);58 }59 await closeWithSuccess(name);60 expect(await result).toBe(name);61 if (withCallback === 'Y') {62 expect(callback).toHaveBeenCalledWith(null, name);63 }64 });...
Using AI Code Generation
1var backstop = require('backstopjs');2backstop('reference', {3})4 .then(function () {5 return backstop('test', {6 });7 })8 .then(function () {9 console.log('done');10 })11 .catch(function (err) {12 console.error(err);13 });14var backstop = require('backstopjs');15backstop('applyPatch', {16})17 .then(function () {18 console.log('done');19 })20 .catch(function (err) {21 console.error(err);22 });23var backstop = require('backstopjs');24backstop('openReport', {25})26 .then(function () {27 console.log('done');28 })29 .catch(function (err) {30 console.error(err);31 });32var backstop = require('backstopjs');33backstop('approve', {34})35 .then(function () {36 console.log('done');37 })38 .catch(function (err) {39 console.error(err);40 });41var backstop = require('backstopjs');42backstop('reject', {43})44 .then(function () {45 console.log('done');46 })47 .catch(function (err) {48 console.error(err);49 });50var backstop = require('backstopjs');51backstop('init', {52})
Using AI Code Generation
1var backstopjs = require('backstopjs');2var config = require('./backstop.json');3backstopjs('test', {config: config})4 .then(function (result) {5 console.log(result);6 })7 .catch(function (err) {8 console.log(err);9 });10{11 {12 },13 {14 },15 {16 }17 {18 }19 "paths": {20 },21}22module.exports = async (page, scenario, vp) => {23 console.log('SCENARIO > ' + scenario.label);24 await require('./onReady')(page, scenario, vp);25};
Using AI Code Generation
1var backstop = require('backstopjs');2var fs = require('fs');3var path = require('path');4var config = require('./backstop.json');5var referenceDir = config.paths.bitmaps_reference;6var testDir = config.paths.bitmaps_test;7var diffDir = config.paths.bitmaps_diff;8var reportDir = config.paths.html_report;9var compareConfig = {10 "compareData": {11 },12 {13 },14 {15 },16 {17 },18 {19 }20};21var referenceFiles = fs.readdirSync(referenceDir);22var testFiles = fs.readdirSync(testDir);23var diffFiles = fs.readdirSync(diffDir);24var reportFiles = fs.readdirSync(reportDir);25var referenceFiles = fs.readdirSync(referenceDir);26var testFiles = fs.readdirSync(testDir);27var diffFiles = fs.readdirSync(diffDir);28var reportFiles = fs.readdirSync(reportDir);29var referenceFiles = fs.readdirSync(referenceDir);30var testFiles = fs.readdirSync(testDir);31var diffFiles = fs.readdirSync(diffDir);32var reportFiles = fs.readdirSync(reportDir);33var referenceFiles = fs.readdirSync(referenceDir);34var testFiles = fs.readdirSync(testDir);35var diffFiles = fs.readdirSync(diffDir);36var reportFiles = fs.readdirSync(reportDir);37var referenceFiles = fs.readdirSync(referenceDir);38var testFiles = fs.readdirSync(testDir);39var diffFiles = fs.readdirSync(diffDir);40var reportFiles = fs.readdirSync(reportDir);41var referenceFiles = fs.readdirSync(referenceDir);42var testFiles = fs.readdirSync(testDir);43var diffFiles = fs.readdirSync(diffDir);44var reportFiles = fs.readdirSync(reportDir);45var referenceFiles = fs.readdirSync(referenceDir);46var testFiles = fs.readdirSync(testDir);
Using AI Code Generation
1const backstop = require("backstopjs");2backstop("test", {3 dockerCommandTemplate: "docker run --rm -it --mount type=bind,source={cwd},target=/src backstopjs/backstopjs:{version} {backstopCommand} {args}",4 engineOptions: {},5 engineOptions: {},6 engineOptions: {},
Using AI Code Generation
1const backstop = require('backstopjs');2const fs = require('fs');3backstop('test', { config: './backstop.json' }).then(function (result) {4 fs.writeFileSync('result.json', JSON.stringify(result, null, 2));5 process.exit(0);6}).catch(function (err) {7 console.error(err);8 process.exit(1);9});10{11 {12 },13 {14 },15 {16 },17 {18 }19 {20 }21 "paths": {22 },23 "engineOptions": {
Using AI Code Generation
1var backstop = require('backstopjs');2var fs = require('fs');3var config = JSON.parse(fs.readFileSync('backstop.json'));4var options = {5};6backstop('test', options).then(function () {7});8{9 {10 },11 {12 },13 {14 }15 {16 }17 "paths": {18 },19 "engineOptions": {20 },21}
Using AI Code Generation
1var backstop = require('backstopjs');2var fs = require('fs');3backstop('test', {4})5.then(function() {6 fs.rename('backstop_data/bitmaps_test/20170630-142516/failed_diff_testpage_0_testpage_0_phone.png', 'backstop_data/bitmaps_test/20170630-142516/failed_diff_testpage_0_testpage_0_phone.png.orig', function(err) {7 if ( err ) console.log('ERROR: ' + err);8 });9})10.catch(function(error) {11 console.log(error);12});13{14 {15 }16 {17 }18 "paths": {19 },20 "engineOptions": {21 },22}
Using AI Code Generation
1var backstop = require('backstopjs');2backstop('reference', {3});4 throw err;5 at Function.Module._resolveFilename (module.js:547:15)6 at Function.Module._load (module.js:474:25)7 at Module.runMain (module.js:693:10)8 at run (bootstrap_node.js:620:7)9 at startup (bootstrap_node.js:
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!!