Best JavaScript code snippet using mountebank
resolve_saved_objects.test.ts
Source:resolve_saved_objects.test.ts
1/*2 * SPDX-License-Identifier: Apache-2.03 *4 * The OpenSearch Contributors require contributions made to5 * this file be licensed under the Apache-2.0 license or a6 * compatible open source license.7 */8/*9 * Licensed to Elasticsearch B.V. under one or more contributor10 * license agreements. See the NOTICE file distributed with11 * this work for additional information regarding copyright12 * ownership. Elasticsearch B.V. licenses this file to you under13 * the Apache License, Version 2.0 (the "License"); you may14 * not use this file except in compliance with the License.15 * You may obtain a copy of the License at16 *17 * http://www.apache.org/licenses/LICENSE-2.018 *19 * Unless required by applicable law or agreed to in writing,20 * software distributed under the License is distributed on an21 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY22 * KIND, either express or implied. See the License for the23 * specific language governing permissions and limitations24 * under the License.25 */26/*27 * Modifications Copyright OpenSearch Contributors. See28 * GitHub history for details.29 */30import {31 resolveSavedObjects,32 resolveIndexPatternConflicts,33 saveObjects,34 saveObject,35} from './resolve_saved_objects';36import { SavedObject, SavedObjectLoader } from '../../../saved_objects/public';37import { IndexPatternsContract } from '../../../data/public';38import { dataPluginMock } from '../../../data/public/mocks';39class SavedObjectNotFound extends Error {40 constructor(options: Record<string, any>) {41 super();42 for (const option in options) {43 if (options.hasOwnProperty(option)) {44 (this as any)[option] = options[option];45 }46 }47 }48}49const openModalMock = jest.fn();50const createObj = (props: Partial<SavedObject>): SavedObject =>51 ({52 ...props,53 } as SavedObject);54describe('resolveSavedObjects', () => {55 describe('resolveSavedObjects', () => {56 it('should take in saved objects and spit out conflicts', async () => {57 const savedObjects = [58 {59 _type: 'search',60 },61 {62 _type: 'index-pattern',63 _id: '1',64 _source: {65 title: 'pattern',66 timeFieldName: '@timestamp',67 },68 },69 {70 _type: 'dashboard',71 },72 {73 _type: 'visualization',74 },75 ];76 const indexPatterns = ({77 get: async () => {78 return {79 create: () => '2',80 };81 },82 create: async () => {83 return '2';84 },85 cache: {86 clear: () => {},87 },88 } as unknown) as IndexPatternsContract;89 const services = ([90 {91 type: 'search',92 get: async () => {93 return {94 applyOpenSearchResp: async () => {},95 save: async () => {96 throw new SavedObjectNotFound({97 savedObjectType: 'index-pattern',98 });99 },100 };101 },102 },103 {104 type: 'dashboard',105 get: async () => {106 return {107 applyOpenSearchResp: async () => {},108 save: async () => {109 throw new SavedObjectNotFound({110 savedObjectType: 'index-pattern',111 });112 },113 };114 },115 },116 {117 type: 'visualization',118 get: async () => {119 return {120 applyOpenSearchResp: async () => {},121 save: async () => {122 throw new SavedObjectNotFound({123 savedObjectType: 'index-pattern',124 });125 },126 };127 },128 },129 ] as unknown) as SavedObjectLoader[];130 const overwriteAll = false;131 const result = await resolveSavedObjects(132 savedObjects,133 overwriteAll,134 services,135 indexPatterns,136 openModalMock137 );138 expect(result.conflictedIndexPatterns.length).toBe(3);139 expect(result.conflictedSavedObjectsLinkedToSavedSearches.length).toBe(0);140 expect(result.conflictedSearchDocs.length).toBe(0);141 });142 it('should bucket conflicts based on the type', async () => {143 const savedObjects = [144 {145 _type: 'search',146 },147 {148 _type: 'index-pattern',149 _id: '1',150 _source: {151 title: 'pattern',152 timeFieldName: '@timestamp',153 },154 },155 {156 _type: 'dashboard',157 },158 {159 _type: 'visualization',160 },161 ];162 const indexPatterns = ({163 get: async () => {164 return {165 create: () => '2',166 };167 },168 create: async () => {169 return '2';170 },171 cache: {172 clear: () => {},173 },174 } as unknown) as IndexPatternsContract;175 const services = ([176 {177 type: 'search',178 get: async () => {179 return {180 applyOpenSearchResp: async () => {},181 save: async () => {182 throw new SavedObjectNotFound({183 savedObjectType: 'search',184 });185 },186 };187 },188 },189 {190 type: 'dashboard',191 get: async () => {192 return {193 applyOpenSearchResp: async () => {},194 save: async () => {195 throw new SavedObjectNotFound({196 savedObjectType: 'index-pattern',197 });198 },199 };200 },201 },202 {203 type: 'visualization',204 get: async () => {205 return {206 savedSearchId: '1',207 applyOpenSearchResp: async () => {},208 save: async () => {209 throw new SavedObjectNotFound({210 savedObjectType: 'index-pattern',211 });212 },213 };214 },215 },216 ] as unknown) as SavedObjectLoader[];217 const overwriteAll = false;218 const result = await resolveSavedObjects(219 savedObjects,220 overwriteAll,221 services,222 indexPatterns,223 openModalMock224 );225 expect(result.conflictedIndexPatterns.length).toBe(1);226 expect(result.conflictedSavedObjectsLinkedToSavedSearches.length).toBe(1);227 expect(result.conflictedSearchDocs.length).toBe(1);228 });229 });230 describe('resolveIndexPatternConflicts', () => {231 let dependencies: Parameters<typeof resolveIndexPatternConflicts>[3];232 beforeEach(() => {233 const search = dataPluginMock.createStartContract().search;234 dependencies = {235 indexPatterns: ({236 get: (id: string) => Promise.resolve({ id }),237 } as unknown) as IndexPatternsContract,238 search,239 };240 });241 it('should resave resolutions', async () => {242 const save = jest.fn();243 const conflictedIndexPatterns = ([244 {245 obj: {246 save,247 },248 doc: {249 _source: {250 kibanaSavedObjectMeta: {251 searchSourceJSON: JSON.stringify({252 index: '1',253 }),254 },255 },256 },257 },258 {259 obj: {260 save,261 },262 doc: {263 _source: {264 kibanaSavedObjectMeta: {265 searchSourceJSON: JSON.stringify({266 index: '3',267 }),268 },269 },270 },271 },272 ] as unknown) as Array<{ obj: SavedObject; doc: any }>;273 const resolutions = [274 {275 oldId: '1',276 newId: '2',277 },278 {279 oldId: '3',280 newId: '4',281 },282 {283 oldId: '5',284 newId: '5',285 },286 ];287 const overwriteAll = false;288 await resolveIndexPatternConflicts(289 resolutions,290 conflictedIndexPatterns,291 overwriteAll,292 dependencies293 );294 expect(save.mock.calls.length).toBe(2);295 expect(save).toHaveBeenCalledWith({ confirmOverwrite: !overwriteAll });296 });297 it('should resolve filter index conflicts', async () => {298 const save = jest.fn();299 const conflictedIndexPatterns = ([300 {301 obj: {302 save,303 },304 doc: {305 _source: {306 kibanaSavedObjectMeta: {307 searchSourceJSON: JSON.stringify({308 index: '1',309 filter: [{ meta: { index: 'filterIndex' } }],310 }),311 },312 },313 },314 },315 {316 obj: {317 save,318 },319 doc: {320 _source: {321 kibanaSavedObjectMeta: {322 searchSourceJSON: JSON.stringify({323 index: '3',324 }),325 },326 },327 },328 },329 ] as unknown) as Array<{ obj: SavedObject; doc: any }>;330 const resolutions = [331 {332 oldId: '1',333 newId: '2',334 },335 {336 oldId: '3',337 newId: '4',338 },339 {340 oldId: 'filterIndex',341 newId: 'newFilterIndex',342 },343 ];344 const overwriteAll = false;345 await resolveIndexPatternConflicts(346 resolutions,347 conflictedIndexPatterns,348 overwriteAll,349 dependencies350 );351 expect(save.mock.calls.length).toBe(2);352 });353 });354 describe('saveObjects', () => {355 it('should save every object', async () => {356 const save = jest.fn();357 const objs = [358 createObj({359 save,360 }),361 createObj({362 save,363 }),364 ];365 const overwriteAll = false;366 await saveObjects(objs, overwriteAll);367 expect(save.mock.calls.length).toBe(2);368 expect(save).toHaveBeenCalledWith({ confirmOverwrite: !overwriteAll });369 });370 });371 describe('saveObject', () => {372 it('should save the object', async () => {373 const save = jest.fn();374 const obj = createObj({375 save,376 });377 const overwriteAll = false;378 await saveObject(obj, overwriteAll);379 expect(save.mock.calls.length).toBe(1);380 expect(save).toHaveBeenCalledWith({ confirmOverwrite: !overwriteAll });381 });382 });...
resolve_saved_objects.js
Source:resolve_saved_objects.js
1/*2 * Licensed to Elasticsearch B.V. under one or more contributor3 * license agreements. See the NOTICE file distributed with4 * this work for additional information regarding copyright5 * ownership. Elasticsearch B.V. licenses this file to you under6 * the Apache License, Version 2.0 (the "License"); you may7 * not use this file except in compliance with the License.8 * You may obtain a copy of the License at9 *10 * http://www.apache.org/licenses/LICENSE-2.011 *12 * Unless required by applicable law or agreed to in writing,13 * software distributed under the License is distributed on an14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY15 * KIND, either express or implied. See the License for the16 * specific language governing permissions and limitations17 * under the License.18 */19import { SavedObjectNotFound } from 'ui/errors';20async function getSavedObject(doc, services) {21 const service = services.find(service => service.type === doc._type);22 if (!service) {23 return;24 }25 const obj = await service.get();26 obj.id = doc._id;27 return obj;28}29function addJsonFieldToIndexPattern(target, sourceString, fieldName, indexName) {30 if (sourceString) {31 try {32 target[fieldName] = JSON.parse(sourceString);33 } catch (error) {34 throw new Error(`Error encountered parsing ${fieldName} for index pattern ${indexName}: ${error.message}`);35 }36 }37}38async function importIndexPattern(doc, indexPatterns, overwriteAll) {39 // TODO: consolidate this is the code in create_index_pattern_wizard.js40 const emptyPattern = await indexPatterns.get();41 const { title, timeFieldName, fields, fieldFormatMap, sourceFilters } = doc._source;42 const importedIndexPattern = {43 id: doc._id,44 title,45 timeFieldName46 };47 addJsonFieldToIndexPattern(importedIndexPattern, fields, 'fields', title);48 addJsonFieldToIndexPattern(importedIndexPattern, fieldFormatMap, 'fieldFormatMap', title);49 addJsonFieldToIndexPattern(importedIndexPattern, sourceFilters, 'sourceFilters', title);50 Object.assign(emptyPattern, importedIndexPattern);51 const newId = await emptyPattern.create(true, !overwriteAll);52 indexPatterns.cache.clear(newId);53 return newId;54}55async function importDocument(obj, doc, overwriteAll) {56 await obj.applyESResp(doc);57 return await obj.save({ confirmOverwrite: !overwriteAll });58}59function groupByType(docs) {60 const defaultDocTypes = {61 searches: [],62 indexPatterns: [],63 other: [],64 };65 return docs.reduce((types, doc) => {66 switch (doc._type) {67 case 'search':68 types.searches.push(doc);69 break;70 case 'index-pattern':71 types.indexPatterns.push(doc);72 break;73 default:74 types.other.push(doc);75 }76 return types;77 }, defaultDocTypes);78}79async function awaitEachItemInParallel(list, op) {80 return await Promise.all(list.map(item => op(item)));81}82export async function resolveIndexPatternConflicts(83 resolutions,84 conflictedIndexPatterns,85 overwriteAll86) {87 let importCount = 0;88 await awaitEachItemInParallel(conflictedIndexPatterns, async ({ obj }) => {89 let oldIndexId = obj.searchSource.getOwnField('index');90 // Depending on the object, this can either be the raw id or the actual index pattern object91 if (typeof oldIndexId !== 'string') {92 oldIndexId = oldIndexId.id;93 }94 const resolution = resolutions.find(({ oldId }) => oldId === oldIndexId);95 if (!resolution) {96 // The user decided to skip this conflict so do nothing97 return;98 }99 const newIndexId = resolution.newId;100 await obj.hydrateIndexPattern(newIndexId);101 if (await saveObject(obj, overwriteAll)) {102 importCount++;103 }104 });105 return importCount;106}107export async function saveObjects(objs, overwriteAll) {108 let importCount = 0;109 await awaitEachItemInParallel(110 objs,111 async obj => {112 if (await saveObject(obj, overwriteAll)) {113 importCount++;114 }115 }116 );117 return importCount;118}119export async function saveObject(obj, overwriteAll) {120 return await obj.save({ confirmOverwrite: !overwriteAll });121}122export async function resolveSavedSearches(123 savedSearches,124 services,125 indexPatterns,126 overwriteAll127) {128 let importCount = 0;129 await awaitEachItemInParallel(savedSearches, async searchDoc => {130 const obj = await getSavedObject(searchDoc, services);131 if (!obj) {132 // Just ignore?133 return;134 }135 if (await importDocument(obj, searchDoc, overwriteAll)) {136 importCount++;137 }138 });139 return importCount;140}141export async function resolveSavedObjects(142 savedObjects,143 overwriteAll,144 services,145 indexPatterns146) {147 const docTypes = groupByType(savedObjects);148 // Keep track of how many we actually import because the user149 // can cancel an override150 let importedObjectCount = 0;151 // Keep a record of any objects which fail to import for unknown reasons.152 const failedImports = [];153 // Start with the index patterns since everything is dependent on them154 await awaitEachItemInParallel(155 docTypes.indexPatterns,156 async indexPatternDoc => {157 try {158 const importedIndexPatternId = await importIndexPattern(indexPatternDoc, indexPatterns, overwriteAll);159 if (importedIndexPatternId) {160 importedObjectCount++;161 }162 } catch (error) {163 failedImports.push({ indexPatternDoc, error });164 }165 }166 );167 // We want to do the same for saved searches, but we want to keep them separate because they need168 // to be applied _first_ because other saved objects can be dependent on those saved searches existing169 const conflictedSearchDocs = [];170 // Keep a record of the index patterns assigned to our imported saved objects that do not171 // exist. We will provide a way for the user to manually select a new index pattern for those172 // saved objects.173 const conflictedIndexPatterns = [];174 // It's possible to have saved objects that link to saved searches which then link to index patterns175 // and those could error out, but the error comes as an index pattern not found error. We can't resolve176 // those the same as way as normal index pattern not found errors, but when those are fixed, it's very177 // likely that these saved objects will work once resaved so keep them around to resave them.178 const conflictedSavedObjectsLinkedToSavedSearches = [];179 await awaitEachItemInParallel(docTypes.searches, async searchDoc => {180 const obj = await getSavedObject(searchDoc, services);181 try {182 if (await importDocument(obj, searchDoc, overwriteAll)) {183 importedObjectCount++;184 }185 } catch (error) {186 if (error instanceof SavedObjectNotFound) {187 if (error.savedObjectType === 'index-pattern') {188 conflictedIndexPatterns.push({ obj, doc: searchDoc });189 } else {190 conflictedSearchDocs.push(searchDoc);191 }192 } else {193 failedImports.push({ obj, error });194 }195 }196 });197 await awaitEachItemInParallel(docTypes.other, async otherDoc => {198 const obj = await getSavedObject(otherDoc, services);199 try {200 if (await importDocument(obj, otherDoc, overwriteAll)) {201 importedObjectCount++;202 }203 } catch (error) {204 if (error instanceof SavedObjectNotFound) {205 if (error.savedObjectType === 'search') {206 failedImports.push({ obj, error });207 }208 if (error.savedObjectType === 'index-pattern') {209 if (obj.savedSearchId) {210 conflictedSavedObjectsLinkedToSavedSearches.push(obj);211 } else {212 conflictedIndexPatterns.push({ obj, doc: otherDoc });213 }214 }215 } else {216 failedImports.push({ obj, error });217 }218 }219 });220 return {221 conflictedIndexPatterns,222 conflictedSavedObjectsLinkedToSavedSearches,223 conflictedSearchDocs,224 importedObjectCount,225 failedImports,226 };...
Using AI Code Generation
1var mb = require('mountebank');2 {3 {4 {5 equals: {6 }7 }8 {9 is: {10 }11 }12 }13 }14];15mb.create({16}, function (error) {17 if (error) {18 console.error(error);19 } else {20 console.log('Mountebank started');21 mb.overwriteAll(imposters, function (error) {22 if (error) {23 console.error(error);24 } else {25 console.log('Imposters created');26 }27 });28 }29});
Using AI Code Generation
1const mb = require('mountebank');2const imposter = {3 {4 {5 is: {6 }7 }8 }9};10mb.create(imposter).then(function (response) {11 console.log(response);12 return mb.overwriteAll(imposter);13}).then(function (response) {14 console.log(response);15});16{ port: 8080, protocol: 'http' }17{ port: 8080, protocol: 'http' }18const mb = require('mountebank');19mb.deleteAll().then(function (response) {20 console.log(response);21});22{ all: true }
Using AI Code Generation
1var mountebank = require('mountebank'),2 mb = mountebank.create({ port: 2525, pidfile: 'mb.pid', logfile: 'mb.log' });3mb.start();4mb.post('/imposters', {5 stubs: [{6 responses: [{7 is: {8 }9 }]10 }]11}, function (error, response) {12 console.log('Imposter created', response.body);13 mb.put('/imposters/3000', {14 stubs: [{15 responses: [{16 is: {17 }18 }]19 }]20 }, function (error, response) {21 console.log('Imposter updated', response.body);22 mb.del('/imposters/3000', function (error, response) {23 console.log('Imposter deleted', response.body);24 mb.stop();25 });26 });27});
Using AI Code Generation
1var mb = require('mountebank');2mb.create({port: 2525, pidfile: 'mb.pid', logfile: 'mb.log', ipWhitelist: ['*']}, function (error, server) {3 if (error) {4 console.log(error);5 } else {6 console.log('Mountebank started on port 2525');7 var stub = {8 {9 equals: {10 }11 }12 {13 is: {14 }15 }16 };17 var imposter = {18 };19 server.createImposter(imposter, function (error, imposter) {20 if (error) {21 console.log(error);22 } else {23 console.log('Imposter created on port 3000');24 server.getImposter(3000, function (error, imposter) {25 if (error) {26 console.log(error);27 } else {28 console.log('Imposter retrieved');29 imposter.overwriteAllStubs([stub], function (error, imposter) {30 if (error) {31 console.log(error);32 } else {33 console.log('Stubs overwritten');34 server.deleteImposter(3000, function (error) {35 if (error) {36 console.log(error);37 } else {38 console.log('Imposter deleted');39 server.stop(function (error) {40 if (error) {41 console.log(error);42 } else {43 console.log('Mountebank stopped');44 }45 });46 }47 });48 }49 });50 }51 });52 }53 });54 }55});
Using AI Code Generation
1var http = require('http');2var mb = require('mountebank');3mb.create({ port: 2525, allowInjection: true }, function (error, server) {4 console.log('mb server created');5 console.log('mb server port : ' + server.port);6 var imposters = [{7 stubs: [{8 responses: [{9 is: {10 headers: { 'Content-Type': 'application/json' },11 body: JSON.stringify({ 'message': 'Hello World' })12 }13 }]14 }]15 }];16 server.overwriteAll(imposters, function (error, imposters) {17 console.log('overwritten');18 console.log(imposters);19 });20});21var http = require('http');22var mb = require('mountebank');23mb.create({ port: 2525, allowInjection: true }, function (error, server) {24 console.log('mb server created');25 console.log('mb server port : ' + server.port);26 var imposters = [{27 stubs: [{28 responses: [{29 is: {30 headers: { 'Content-Type': 'application/json' },31 body: JSON.stringify({ 'message': 'Hello World' })32 }33 }]34 }]35 }];36 server.create(imposters, function (error, imposters) {37 console.log('created');38 console.log(imposters);39 });40});41var http = require('http');42var mb = require('mountebank');43mb.create({ port: 2525, allowInjection: true }, function (error, server) {44 console.log('mb server created');45 console.log('mb server port : ' + server.port);46 var imposters = [{47 stubs: [{48 responses: [{49 is: {50 headers: { 'Content-Type': 'application/json' },51 body: JSON.stringify({ 'message': 'Hello World' })52 }53 }]54 }]55 }];56 server.get(function (error
Using AI Code Generation
1var mb = require('mountebank');2var mbHelper = require('mountebank-helper');3var imposter = require('./imposter.js');4var mbServer = mb.create({port:2525, pidfile:'mb.pid', logfile:'mb.log'});5mbServer.start()6.then(function(){7 return mbHelper.postImposter(imposter, 2525);8})9.then(function(){10 console.log('Imposter created');11})12.catch(function(err){13 console.log(err);14});15var imposter = {16 {17 {18 "is": {19 "headers": {20 },21 }22 }23 }24};25module.exports = imposter;26var mb = require('mountebank');27var mbHelper = require('mountebank-helper');28var imposter = require('./imposter.js');29var mbServer = mb.create({port:2525, pidfile:'mb.pid', logfile:'mb.log'});30mbServer.start()31.then(function(){32 return mbHelper.overwriteAll(imposter, 2525);33})34.then(function(){35 console.log('Imposter created');36})37.catch(function(err){38 console.log(err);39});40var imposter = {41 {42 {43 "is": {44 "headers": {45 },46 }47 }48 }49};50module.exports = imposter;
Using AI Code Generation
1var mb = require('mountebank');2var fs = require('fs');3var imposter = JSON.parse(fs.readFileSync('imposter.json', 'utf8'));4var stubs = JSON.parse(fs.readFileSync('stubs.json', 'utf8'));5mb.create({'port': 2525}, function () {6 mb.overwriteAll('http', 2525, imposter, stubs, function () {7 console.log('overwritten');8 });9});10{11}12 {13 {14 "is": {15 "headers": {16 },17 "body": "{ \"message\": \"Hello World\" }"18 }19 }20 }
Using AI Code Generation
1var mb = require('mountebank'),2 assert = require('assert'),3 Q = require('q'),4 fs = require('fs'),5 imposter = require('./imposter.json'),6 imposters = require('./imposters.json');7function getImposter(imposterId){8 var deferred = Q.defer();9 mb.get(mbServer + '/imposters/' + imposterId, function (error, response) {10 if (error) {11 deferred.reject(error);12 }13 else {14 deferred.resolve(response.body);15 }16 });17 return deferred.promise;18}19function createImposter(imposter){20 var deferred = Q.defer();21 mb.post(mbServer + '/imposters', imposter, function (error, response) {22 if (error) {23 deferred.reject(error);24 }25 else {26 deferred.resolve(response.body);27 }28 });29 return deferred.promise;30}31function deleteImposter(imposterId){32 var deferred = Q.defer();33 mb.del(mbServer + '/imposters/' + imposterId, function (error, response) {34 if (error) {35 deferred.reject(error);36 }37 else {38 deferred.resolve(response.body);39 }40 });41 return deferred.promise;42}43function overwriteImposter(imposterId, imposter){44 var deferred = Q.defer();45 mb.put(mbServer + '/imposters/' + imposterId, imposter, function (error, response) {46 if (error) {47 deferred.reject(error);48 }49 else {50 deferred.resolve(response.body);51 }52 });53 return deferred.promise;54}55function overwriteAll(imposters){56 var deferred = Q.defer();57 mb.put(mbServer + '/imposters', imposters, function (error, response) {58 if (error) {59 deferred.reject(error);60 }61 else {62 deferred.resolve(response.body);63 }64 });65 return deferred.promise;66}67function checkImposter(im
Using AI Code Generation
1var fs = require('fs');2var request = require('request');3var stubs = fs.readFileSync('stubs.json', 'utf8');4console.log(stubs);5var port = 2525;6var options = {7 headers: {8 },9};10var callback = function(error, response, body) {11 console.log(response.statusCode);12 console.log(body);13};14request(options, callback);15var fs = require('fs');16var request = require('request');17var stubs = fs.readFileSync('stubs.json', 'utf8');18console.log(stubs);19var port = 2525;20var options = {21 headers: {22 },23};24var callback = function(error, response, body) {25 console.log(response.statusCode);26 console.log(body);27};28request(options, callback);29var request = require('request');30var port = 2525;
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!!