Best JavaScript code snippet using playwright-internal
submissions.js
Source:submissions.js
...47 };48 49 //Initialize all the AJAX form events.50 run = function () {51 console.log(getCurrentTime() + " [js/submissions.js] (run) - start");52 //Fetches the initial contact data53 CONTACTS.submissions.submitCreate();54 CONTACTS.submissions.submitUpdate();55 CONTACTS.submissions.deleteContact();56 console.log(getCurrentTime() + " [js/submissions.js] (run) - end");57 };58 59 /**60 * Attempts to register a new contact using a JAX-RS POST. 61 */62 CONTACTS.submissions.submitCreate = function() {63 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - start");64 65 $("#contacts-add-form").submit(function(event) {66 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate - submit event) - checking if the form is valid");67 // Ensure that the form has been validated.68 CONTACTS.validation.addContactsFormValidator.form();69 // If there are any validation error then don't process the submit. 70 if (CONTACTS.validation.addContactsFormValidator.valid()){71 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate - submit event) - started");72 event.preventDefault();73 74 /*75 * There is a bug in the jQM that causes the list of results on the main page to be blank the first 76 * time you submit a form. After the first time the list display correctly. This is caused when 77 * the first div gets removed by jQM and added back. This creates a problem for the very first time.78 * That is because jQM does not apply the listener to 'getContacts()' to the new page because there are 79 * 2 versions of the first div in the DOM at one time. So if we remove the div early it acts like it80 * does every time after the first. It gets the listener applied and it will fired at the right time.81 * 82 * TODO: If jQM fixes this issue in the future then this line can be removed.83 * 84 * UDPATE: After we figured out what the cause was, we implemented a fix for the validation process that 85 * fixed this problem as a side effect. In order to display errors that are sent back from the server86 * side validation we need to keep the form from transitioning. We do that by adding 'data-ajax="false"'87 * to the form in the HTML. Then we manually change the page when the form is successfully submitted. 88 * Since we are manually changing the page we avoid this issue altogether. 89 */ 90// $('#contacts-list-page').remove();91 92 // Transform the form fields into JSON.93 // Must pull from the specific form so that we get the right data in case another form has data in it.94 var serializedForm = $("#contacts-add-form").serializeObject();95 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate - submit event) - serializedForm.birthDate = " + serializedForm.birthDate);96 // Turn the object into a String.97 var contactData = JSON.stringify(serializedForm);98 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate - submit event) - contactData = " + contactData);99 100 /* The jQuery XMLHttpRequest (jqXHR) object returned by $.ajax() as of jQuery 1.5 is a superset of101 * the browser's native XMLHttpRequest object. For example, it contains responseText and responseXML102 * properties, as well as a getResponseHeader() method. When the transport mechanism is something103 * other than XMLHttpRequest (for example, a script tag for a JSONP request) the jqXHR object104 * simulates native XHR functionality where possible.105 *106 * The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving107 * them all the properties, methods, and behavior of a Promise (see Deferred object for more108 * information). These methods take one or more function arguments that are called when the109 * $.ajax() request terminates. This allows you to assign multiple callbacks on a single request,110 * and even to assign callbacks after the request may have completed. (If the request is already111 * complete, the callback is fired immediately.)112 */113 var jqxhr = $.ajax({114 url: restEndpoint,115 contentType: "application/json",116// dataType: "json",117 data: contactData,118 type: "POST"119 }).done(function(data, textStatus, jqXHR) {120 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - ajax done");121 122 // Reset this flag when the form passes validation. 123 CONTACTS.validation.formEmail = null;124 125 // Clear the form or else the next time you go to add a contact the last one will still be there.126 $('#contacts-add-form')[0].reset();127 128 // Remove errors display as a part of the validation system.129 $('.invalid').remove();130 131 // Because we turned off the automatic page transition to catch server side error we need to do it ourselves.132 $("body").pagecontainer("change", "#contacts-list-page");133 134 }).fail(function(jqXHR, textStatus, errorThrown) {135 // Remove any errors that are not a part of the validation system.136 $('.invalid').remove();137 138 // Check for server side validation errors. This should catch the email uniqueness validation.139 if ((jqXHR.status === 409) || (jqXHR.status === 400)) {140 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - error in ajax - " +141 "Validation error updating contact! " + jqXHR.status);142// console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - error in ajax" +143// " - jqXHR = " + jqXHR.status +144// ", textStatus = " + textStatus +145// ", errorThrown = " + errorThrown +146// ", responseText = " + jqXHR.responseText);147 148 // Get the contact.149 var contact = $("#contacts-add-form")[0];150 151 // Extract the error messages from the server.152 var errorMsg = $.parseJSON(jqXHR.responseText);153 154 // We only want to set this flag if there is actual email error.155 $.each(errorMsg, function(index, val) {156 if (index === 'email'){157 // Get the contact email and set it for comparison in the form validation.158 $.each(contact, function(index, val){159 // This will look for an element with the name of 'email' and pull it's value.160 if (val.name == "email"){161 CONTACTS.validation.formEmail = val.value;162 return false;163 }164 });165 }166 });167 168 // Apply the error to the form.169 CONTACTS.validation.displayServerSideErrors("#contacts-add-form", errorMsg);170 171 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - error in ajax - " +172 "Validation error displayed in the form for the user to fix! ");173 } else if (jqXHR.status >= 200 && jqXHR.status < 300 || jqXHR.status === 304) {174 // It should not reach this error as long as the dataType: is not set. Or if it is set to something175 // like JSON then the Server method must return data.176 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - ajax error on 20x with error message: "177 + errorThrown.message);178 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - ajax error because the REST service doesn't return" +179 "any data and this app expects data. Fix the REST app or remove the 'dataType:' option from the AJAX call.");180 181 // Extract the error messages from the server.182 var errorMsg = $.parseJSON(jqXHR.responseText);183 184 // Apply the error to the form.185 CONTACTS.validation.displayServerSideErrors("#contacts-add-form", errorMsg);186 187 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - ajax error on 20x - " +188 "after displayServerSideErrors()");189 } else {190 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - error in ajax" +191 " - jqXHR = " + jqXHR.status +192 ", textStatus = " + textStatus +193 ", errorThrown = " + errorThrown +194 ", responseText = " + jqXHR.responseText);195 196 // Extract the error messages from the server.197 var errorMsg = $.parseJSON(jqXHR.responseText);198 199 // Apply the error to the form.200 CONTACTS.validation.displayServerSideErrors("#contacts-add-form", errorMsg);201 }202 });203 }204 });205 console.log(getCurrentTime() + " [js/submissions.js] (submitCreate) - end");206 };207 208 /**209 * Attempts to update a contact using a JAX-RS PUT. 210 */211 CONTACTS.submissions.submitUpdate = function() {212 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - start");213 214 $("#contacts-edit-form").submit(function(event) {215 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate - submit event) - checking if the form is valid");216 217 // Ensure that the form has been validated.218 CONTACTS.validation.editContactsFormValidator.form();219 // If there are any validation error then don't process the submit. 220 if (CONTACTS.validation.editContactsFormValidator.valid()){221 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate - submit event) - started");222 event.preventDefault();223 224 /*225 * There is a bug in the jQM that causes the list of results on the main page to be blank the first 226 * time you submit a form. After the first time the list display correctly. This is caused when 227 * the first div gets removed by jQM and added back. This creates a problem for the very first time.228 * That is because jQM does not apply the listener to 'getContacts()' to the new page because there are 229 * 2 versions of the first div in the DOM at one time. So if we remove the div early it acts like it230 * does every time after the first. It gets the listener applied and it will fired at the right time.231 * 232 * TODO: If jQM fixes this issue in the future then this line can be removed.233 * 234 * UDPATE: After we figured out what the cause was, we implemented a fix for the validation process that 235 * fixed this problem as a side effect. In order to display errors that are sent back from the server236 * side validation we need to keep the form from transitioning. We do that by adding 'data-ajax="false"'237 * to the form in the HTML. Then we manually change the page when the form is successfully submitted. 238 * Since we are manually changing the page we avoid this issue altogether. 239 */ 240// $('#contacts-list-page').remove();241 // Obtain the contact ID, to use in constructing the REST URI.242 var contactId = $("#contacts-edit-input-id").val();243 // Transform the form fields into JSON.244 // Must pull from the specific form so that we get the right data in case another form has data in it.245 var serializedForm = $("#contacts-edit-form").serializeObject();246 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate - submit event) - serializedForm.birthDate = " + serializedForm.birthDate);247 // Turn the object into a String.248 var contactData = JSON.stringify(serializedForm);249 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate - submit event) - contactData = " + contactData);250 251 /* The jQuery XMLHttpRequest (jqXHR) object returned by $.ajax() as of jQuery 1.5 is a superset of252 * the browser's native XMLHttpRequest object. For example, it contains responseText and responseXML253 * properties, as well as a getResponseHeader() method. When the transport mechanism is something254 * other than XMLHttpRequest (for example, a script tag for a JSONP request) the jqXHR object255 * simulates native XHR functionality where possible.256 *257 * The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving258 * them all the properties, methods, and behavior of a Promise (see Deferred object for more259 * information). These methods take one or more function arguments that are called when the260 * $.ajax() request terminates. This allows you to assign multiple callbacks on a single request,261 * and even to assign callbacks after the request may have completed. (If the request is already262 * complete, the callback is fired immediately.)263 */264 var jqxhr = $.ajax({265 url: restEndpoint + "/" + contactId,266 contentType: "application/json",267 dataType: "json",268 data: contactData,269 type: "PUT"270 }).done(function(data, textStatus, jqXHR) {271 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - ajax done");272 273 // Reset this flag when the form passes validation. 274 CONTACTS.validation.formEmail = null;275 276 // Remove errors display as a part of the validation system.277 $('.invalid').remove();278 279 // Because we turned off the automatic page transition to catch server side error we need to do it ourselves.280 $("body").pagecontainer("change", "#contacts-list-page");281 282 }).fail(function(jqXHR, textStatus, errorThrown) {283 // Remove any errors that are not a part of the validation system.284 $('.invalid').remove();285 286 // Check for server side validation errors. This should catch the email uniqueness validation.287 if ((jqXHR.status === 409) || (jqXHR.status === 400)) {288 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - error in ajax - " +289 "Validation error updating contact! " + jqXHR.status);290// console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - error in ajax" +291// " - jqXHR = " + jqXHR.status +292// ", textStatus = " + textStatus +293// ", errorThrown = " + errorThrown +294// ", responseText = " + jqXHR.responseText);295 296 // Get the contact.297 var contact = $("#contacts-edit-form")[0];298 299 // Extract the error messages from the server.300 var errorMsg = $.parseJSON(jqXHR.responseText);301 302 // We only want to set this flag if there is actual email error.303 $.each(errorMsg, function(index, val) {304 if (index === 'email'){305 // Get the contact email and set it for comparison in the form validation.306 $.each(contact, function(index, val){307 // This will look for an element with the name of 'email' and pull it's value.308 if (val.name == "email"){309 CONTACTS.validation.formEmail = val.value;310 return false;311 }312 });313 }314 });315 316 // Apply the error to the form.317 CONTACTS.validation.displayServerSideErrors("#contacts-edit-form", errorMsg);318 319 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - error in ajax - " +320 "Validation error displayed in the form for the user to fix! ");321 } else if (jqXHR.status >= 200 && jqXHR.status < 300 || jqXHR.status === 304) {322 // It should not reach this error as long as the dataType: is not set. Or if it is set to something323 // like JSON then the Server method must return data.324 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - ajax error on 20x with error message: "325 + errorThrown.message);326 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - ajax error because the REST service doesn't return" +327 "any data and this app expects data. Fix the REST app or remove the 'dataType:' option from the AJAX call.");328 329 // Extract the error messages from the server.330 var errorMsg = $.parseJSON(jqXHR.responseText);331 332 // Apply the error to the form.333 CONTACTS.validation.displayServerSideErrors("#contacts-edit-form", errorMsg);334 335 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - ajax error on 20x - " +336 "after displayServerSideErrors()");337 } else {338 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - error in ajax" +339 " - jqXHR = " + jqXHR.status +340 ", textStatus = " + textStatus +341 ", errorThrown = " + errorThrown +342 ", responseText = " + jqXHR.responseText);343 344 // Extract the error messages from the server.345 var errorMsg = $.parseJSON(jqXHR.responseText);346 347 // Apply the error to the form.348 CONTACTS.validation.displayServerSideErrors("#contacts-edit-form", errorMsg);349 }350 });351 }352 });353 console.log(getCurrentTime() + " [js/submissions.js] (submitUpdate) - end");354 };355 356 /**357 * Attempts to delete a contact using a JAX-RS DELETE. 358 */359 CONTACTS.submissions.deleteContact = function() {360 console.log(getCurrentTime() + " [js/submissions.js] (deleteContact) - start");361 362 $("#confirm-delete-button").click(function(event) {363 console.log(getCurrentTime() + " [js/submissions.js] (deleteContact - submit event) - started");364 // You must not preventDefault on a click on a link as that will prevent it from changing pages. 365// event.preventDefault();366 // Obtain the contact ID, to use in constructing the REST URI.367 var contactId = $("#contacts-edit-input-id").val();368 369 /* The jQuery XMLHttpRequest (jqXHR) object returned by $.ajax() as of jQuery 1.5 is a superset of370 * the browser's native XMLHttpRequest object. For example, it contains responseText and responseXML371 * properties, as well as a getResponseHeader() method. When the transport mechanism is something372 * other than XMLHttpRequest (for example, a script tag for a JSONP request) the jqXHR object373 * simulates native XHR functionality where possible.374 *375 * The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving376 * them all the properties, methods, and behavior of a Promise (see Deferred object for more377 * information). These methods take one or more function arguments that are called when the378 * $.ajax() request terminates. This allows you to assign multiple callbacks on a single request,379 * and even to assign callbacks after the request may have completed. (If the request is already380 * complete, the callback is fired immediately.)381 */382 var jqxhr = $.ajax({383 url: restEndpoint + "/" + contactId,384 contentType: "application/json",385 type: "DELETE"386 }).done(function(data, textStatus, jqXHR) {387 console.log(getCurrentTime() + " [js/submissions.js] (deleteContact) - ajax done");388 389 // Reset this flag when the form passes validation. 390 CONTACTS.validation.formEmail = null;391 392 // Remove errors display as a part of the validation system. 393 CONTACTS.validation.editContactsFormValidator.resetForm();394 395 // Remove errors display as a part of the validation system.396 $('.invalid').remove();397 398 }).fail(function(jqXHR, textStatus, errorThrown) {399 // Remove any errors that are not a part of the validation system.400 $('.invalid').remove();401 402 console.log(getCurrentTime() + " [js/submissions.js] (deleteContact) - error in ajax" +403 " - jqXHR = " + jqXHR.status +404 ", textStatus = " + textStatus +405 ", errorThrown = " + errorThrown +406 ", responseText = " + jqXHR.responseText);407 408 // Extract the error messages from the server.409 var errorMsg = $.parseJSON(jqXHR.responseText);410 411 // Apply the error to the form.412 CONTACTS.validation.displayServerSideErrors("#contacts-edit-form", errorMsg);413 });414 });415 console.log(getCurrentTime() + " [js/submissions.js] (deleteContact) - end");416 };417 418 //Set up each of these event listeners.419 run();...
app.js
Source:app.js
...38 //Initialize the vars in the beginning so that you will always have access to them.39 var getCurrentTime = CONTACTS.util.getCurrentTime,40 restEndpoint = CONTACTS.app.restEndpoint;41 42 console.log(getCurrentTime() + " [js/app.js] (document -> pagecreate) - start");43 44 /* 45 * Make sure the Contacts list gets populated but only once.46 * 47 * The "pagebeforeshow" event will delay this function until everything is set up.48 * 49 * Because of the interesting jQM loading architecture, multiple event triggering is a constant problem. 50 * The "e.handled" if statement used here and elsewhere is meant to keep jQM from running this code multiple 51 * times for one display. 52 */53 $('#contacts-list-page').on( "pagebeforeshow", function(e) {54 if(e.handled !== true) {55 console.log(getCurrentTime() + " [js/app.js] (#contacts-list-page -> pagebeforeshow) - start");56 57 // Fetches the initial Contact data.58 CONTACTS.app.getContacts();59 60 e.handled = true;61 console.log(getCurrentTime() + " [js/app.js] (#contacts-list-page -> pagebeforeshow) - end");62 }63 });64 65 // This is called on 'pagebeforeshow' above and by the CONTACTS.submissions66 // Uses JAX-RS GET to retrieve current contact list. 67 CONTACTS.app.getContacts = function () {68 console.log(getCurrentTime() + " [js/app.js] (getContacts) - start");69 var jqxhr = $.ajax({70 url: restEndpoint,71 cache: false,72 type: "GET"73 }).done(function(data, textStatus, jqXHR) {74 console.log(getCurrentTime() + " [js/app.js] (getContacts) - succes on ajax call");75 CONTACTS.app.buildContactList(data);76 }).fail(function(jqXHR, textStatus, errorThrown) {77 console.log(getCurrentTime() + " [js/app.js] (getContacts) - error in ajax - " +78 " - jqXHR = " + jqXHR.status +79 " - textStatus = " + textStatus +80 " - errorThrown = " + errorThrown);81 });82 console.log(getCurrentTime() + " [js/app.js] (getContacts) - end");83 };84 // This is called by CONTACTS.app.getContacts.85 // Display contact list on page one.86 CONTACTS.app.buildContactList = function (contacts) {87 console.log(getCurrentTime() + " [js/app.js] (buildContactList) - start");88 var contactList = "",89 contactDetailList = "";90 91 // The data from the AJAX call is not sorted alphabetically, this will fix that.92 contacts.sort(function(a,b){93 var aName = a.firstName.toLowerCase() + a.lastName.toLowerCase();94 var bName = b.firstName.toLowerCase() + b.lastName.toLowerCase(); 95 return ((aName < bName) ? -1 : ((aName > bName) ? 1 : 0));96 });97 98 // Pull the info out of the Data returned from the AJAX request and create the HTML to be placed on the page.99 $.each(contacts, function(index, contact) {100 // Create the HTML for the List only view.101 contactList = contactList.concat(102 "<li id=list-contact-ID-" + contact.id.toString() + " class=contacts-list-item >" +103 "<a href='#contacts-edit-page' >" + contact.firstName.toString() + " " + contact.lastName.toString() + "</a>" +104 "</li>");105 // Create the HTML for the Detailed List view.106 contactDetailList = contactDetailList.concat(107 "<li id=detail-contact-ID-" + contact.id.toString() + " class=contacts-detail-list-item >" +108 "<a href='#contacts-edit-page' >" + contact.firstName.toString() + " " + contact.lastName.toString() + "</a>" +109 "<div class='detialedList'>" +110 "<p><strong>" + contact.email.toString() + "</strong></p>" +111 "<p>" + contact.phoneNumber.toString() + "</p>" +112 "<p>" + contact.birthDate.toString() + "</p>" +113 "</div>" +114 "</li>");115 });116 117 // Start with a clean list element otherwise we would have repeats.118 $('#contacts-display-listview').empty();119 120 // Check if it is already initialized or not, refresh the list in case it is initialized otherwise trigger create.121 if ( $('#contacts-display-listview').hasClass('ui-listview')) {122 console.log(getCurrentTime() + " [js/app.js] (#contacts-display-listview - hasClass ui-listview) - append.listview - start");123 $('#contacts-display-listview').append(contactList).listview("refresh", true);124 console.log(getCurrentTime() + " [js/app.js] (#contacts-display-listview - hasClass ui-listview) - append.listview - end");125 } 126 else {127 console.log(getCurrentTime() + " [js/app.js] (#contacts-display-listview - !hasClass ui-listview) - append.trigger - start");128 $('#contacts-display-listview').append(contactList).enhanceWithin();129 console.log(getCurrentTime() + " [js/app.js] (#contacts-display-listview - !hasClass ui-listview) - append.trigger - end");130 } 131 132 // Start with a clean list element otherwise we would have repeats.133 $('#contacts-display-detail-listview').empty();134 135 // check if it is already initialized or not, refresh the list in case it is initialized otherwise trigger create136 if ( $('#contacts-display-detail-listview').hasClass('ui-listview')) {137 console.log(getCurrentTime() + " [js/app.js] (#contacts-display-detail-listview - hasClass ui-listview) - append.listview - start");138 $('#contacts-display-detail-listview').append(contactDetailList).listview("refresh", true);139 console.log(getCurrentTime() + " [js/app.js] (#contacts-display-detail-listview - hasClass ui-listview) - append.listview - end");140 } 141 else {142 console.log(getCurrentTime() + " [js/app.js] (#contacts-display-detail-listview - !hasClass ui-listview) - append.trigger - start");143 $('#contacts-display-detail-listview').append(contactDetailList).enhanceWithin();144 console.log(getCurrentTime() + " [js/app.js] (#contacts-display-detail-listview - !hasClass ui-listview) - append.trigger - end");145 } 146 147 // Attach onclick event to each row of the contact list that will open up the contact info to be edited.148 $('.contacts-list-item').on("click", function(event){149 if(event.handled !== true) {150 console.log(getCurrentTime() + " [js/app.js] (.contacts-display-listview -> on click) - start");151 152 CONTACTS.app.getContactById($(this).attr("id").split("list-contact-ID-").pop());153 154 event.handled = true;155 console.log(getCurrentTime() + " [js/app.js] (.contacts-display-listview -> on click) - end");156 }157 });158 159 // Attach onclick event to each row of the contact list detailed page that will open up the contact info to be edited.160 $('li.contacts-detail-list-item').on("click", function(event){161 if(event.handled !== true) {162 console.log(getCurrentTime() + " [js/app.js] (li.contacts-display-listview -> on click) - start");163 164 CONTACTS.app.getContactById($(this).attr("id").split("detail-contact-ID-").pop());165 166 // Turn the whole <li> into a link.167 $("body").pagecontainer("change", "#contacts-edit-page");168 169 event.handled = true;170 console.log(getCurrentTime() + " [js/app.js] (li.contacts-display-listview -> on click) - end");171 }172 });173 174 console.log(getCurrentTime() + " [js/app.js] (buildContactList) - end");175 // Add in a line to visually see when we are done.176 console.log("-----------------------------List Page---------------------------------------");177 };178 179 // This is called by the on click event list above.180 // Retrieve employee detail based on employee id.181 CONTACTS.app.getContactById = function (contactID) {182 console.log(getCurrentTime() + " [js/app.js] (getContactById) - start");183 console.log(getCurrentTime() + " [js/app.js] (getContactById) - contactID = " + contactID);184 185 var jqxhr = $.ajax({186 url: restEndpoint + "/" + contactID.toString(),187 cache: false,188 type: "GET"189 }).done(function(data, textStatus, jqXHR) {190 console.log(getCurrentTime() + " [js/app.js] (getContactById) - success on ajax call");191 CONTACTS.app.buildContactDetail(data);192 }).fail(function(jqXHR, textStatus, errorThrown) {193 console.log(getCurrentTime() + " [js/app.js] (getContactById) - error in ajax" +194 " - jqXHR = " + jqXHR.status +195 " - textStatus = " + textStatus +196 " - errorThrown = " + errorThrown);197 });198 console.log(getCurrentTime() + " [js/app.js] (getContactById) - end");199 };200 201 // This is called by CONTACTS.app.getContactById.202 // Display contact detail for editing on the Edit page.203 CONTACTS.app.buildContactDetail = function(contact) {204 console.log(getCurrentTime() + " [js/app.js] (buildContactDetail) - start");205 206 // The intl-Tel-Input plugin stores the lasted used country code and uses it to predetermin the flag to be 207 // displayed. So we remove the plugin before the data gets loaded in the Edit form and then initialize it after. 208 $("#contacts-edit-input-tel").intlTelInput("destroy");209 210 // Put each field value in the text input on the page.211 $('#contacts-edit-input-firstName').val(contact.firstName);212 $('#contacts-edit-input-lastName').val(contact.lastName);213 $('#contacts-edit-input-tel').val(contact.phoneNumber);214 $('#contacts-edit-input-email').val(contact.email);215 $('#contacts-edit-input-date').val(contact.birthDate);216 $('#contacts-edit-input-id').val(contact.id);217 218 // The intl-Tel-Input plugin needs to be initialized everytime the data gets loaded into the Edit form so that 219 // it will correctly validate it and display the correct flag.220 $('#contacts-edit-input-tel').intlTelInput({nationalMode:false});221 222 console.log(getCurrentTime() + " [js/app.js] (buildContactDetail) - end");223 // Add in a line to visually see when we are done.224 console.log("-----------------------------Update Page---------------------------------------");225 };226 227 console.log(getCurrentTime() + " [js/app.js] (document -> pagecreate) - end");...
webaudio.js
Source:webaudio.js
...87 this.scriptNode = this.ac.createJavaScriptNode(bufferSize);88 }89 this.scriptNode.connect(this.ac.destination);90 this.scriptNode.onaudioprocess = function () {91 var time = my.getCurrentTime();92 if (my.buffer && time > my.getDuration()) {93 my.setState(my.FINISHED_STATE);94 } else if (my.buffer && time >= my.scheduledPause) {95 my.setState(my.PAUSED_STATE);96 } else if (my.state === my.states[my.PLAYING_STATE]) {97 my.fireEvent('audioprocess', time);98 }99 };100 },101 createAnalyserNode: function () {102 this.analyser = this.ac.createAnalyser();103 this.analyser.connect(this.gainNode);104 },105 /**106 * Create the gain node needed to control the playback volume.107 */108 createVolumeNode: function () {109 // Create gain node using the AudioContext110 if (this.ac.createGain) {111 this.gainNode = this.ac.createGain();112 } else {113 this.gainNode = this.ac.createGainNode();114 }115 // Add the gain node to the graph116 this.gainNode.connect(this.ac.destination);117 },118 /**119 * Set the gain to a new value.120 *121 * @param {Number} newGain The new gain, a floating point value122 * between 0 and 1. 0 being no gain and 1 being maximum gain.123 */124 setVolume: function (newGain) {125 this.gainNode.gain.value = newGain;126 },127 /**128 * Get the current gain.129 *130 * @returns {Number} The current gain, a floating point value131 * between 0 and 1. 0 being no gain and 1 being maximum gain.132 */133 getVolume: function () {134 return this.gainNode.gain.value;135 },136 decodeArrayBuffer: function (arraybuffer, callback, errback) {137 if (!this.offlineAc) {138 this.offlineAc = this.getOfflineAudioContext(this.ac ? this.ac.sampleRate : 44100);139 }140 this.offlineAc.decodeAudioData(arraybuffer, (function (data) {141 callback(data);142 }).bind(this), errback);143 },144 /**145 * @returns {Array} Array of peaks or array of arrays of peaks.146 */147 getPeaks: function (length) {148 var sampleSize = this.buffer.length / length;149 var sampleStep = ~~(sampleSize / 10) || 1;150 var channels = this.buffer.numberOfChannels;151 var splitPeaks = [];152 var mergedPeaks = [];153 for (var c = 0; c < channels; c++) {154 var peaks = splitPeaks[c] = [];155 var chan = this.buffer.getChannelData(c);156 for (var i = 0; i < length; i++) {157 var start = ~~(i * sampleSize);158 var end = ~~(start + sampleSize);159 var max = 0;160 for (var j = start; j < end; j += sampleStep) {161 var value = chan[j];162 if (value > max) {163 max = value;164 // faster than Math.abs165 } else if (-value > max) {166 max = -value;167 }168 }169 peaks[i] = max;170 if (c == 0 || max > mergedPeaks[i]) {171 mergedPeaks[i] = max;172 }173 }174 }175 return this.params.splitChannels ? splitPeaks : mergedPeaks;176 },177 getPlayedPercents: function () {178 return this.state.getPlayedPercents.call(this);179 },180 disconnectSource: function () {181 if (this.source) {182 this.source.disconnect();183 }184 },185 destroy: function () {186 if (!this.isPaused()) {187 this.pause();188 }189 this.unAll();190 this.buffer = null;191 this.disconnectFilters();192 this.disconnectSource();193 this.gainNode.disconnect();194 this.scriptNode.disconnect();195 this.analyser.disconnect();196 },197 load: function (buffer) {198 this.startPosition = 0;199 this.lastPlay = this.ac.currentTime;200 this.buffer = buffer;201 this.createSource();202 },203 createSource: function () {204 this.disconnectSource();205 this.source = this.ac.createBufferSource();206 //adjust for old browsers.207 this.source.start = this.source.start || this.source.noteGrainOn;208 this.source.stop = this.source.stop || this.source.noteOff;209 this.source.playbackRate.value = this.playbackRate;210 this.source.buffer = this.buffer;211 this.source.connect(this.analyser);212 },213 isPaused: function () {214 return this.state !== this.states[this.PLAYING_STATE];215 },216 getDuration: function () {217 if (!this.buffer) {218 return 0;219 }220 return this.buffer.duration;221 },222 seekTo: function (start, end) {223 this.scheduledPause = null;224 if (start == null) {225 start = this.getCurrentTime();226 if (start >= this.getDuration()) {227 start = 0;228 }229 }230 if (end == null) {231 end = this.getDuration();232 }233 this.startPosition = start;234 this.lastPlay = this.ac.currentTime;235 if (this.state === this.states[this.FINISHED_STATE]) {236 this.setState(this.PAUSED_STATE);237 }238 return { start: start, end: end };239 },240 getPlayedTime: function () {241 return (this.ac.currentTime - this.lastPlay) * this.playbackRate;242 },243 /**244 * Plays the loaded audio region.245 *246 * @param {Number} start Start offset in seconds,247 * relative to the beginning of a clip.248 * @param {Number} end When to stop249 * relative to the beginning of a clip.250 */251 play: function (start, end) {252 console.log("BACKEND PLAY");253 // need to re-create source on each playback254 this.createSource();255 var adjustedTime = this.seekTo(start, end);256 start = adjustedTime.start;257 end = adjustedTime.end;258 this.scheduledPause = end;259 this.source.start(0, start, end - start);260 this.setState(this.PLAYING_STATE);261 },262 /**263 * Pauses the loaded audio.264 */265 // pauseloop: function () {266 // console.log("wavesurfer.getCurrentTime()1aloop: ", wavesurfer.getCurrentTime());267 // this.scheduledPause = null;268 // console.log("wavesurfer.getCurrentTime()1bloop: ", wavesurfer.getCurrentTime());269 //270 // // this.startPosition += this.getPlayedTime();271 // console.log("wavesurfer.getCurrentTime()1cloop: ", wavesurfer.getCurrentTime());272 // this.source && this.source.stop(0);273 // console.log("wavesurfer.getCurrentTime()1dloop: ", wavesurfer.getCurrentTime());274 //275 // this.setState(this.PAUSED_STATE);276 // console.log("wavesurfer.getCurrentTime()1eloop: ", wavesurfer.getCurrentTime());277 // },278 setTime: function (time){279 this.startPosition = time;280 },281 pause: function () {282 console.log("wavesurfer.getCurrentTime()1a: ", wavesurfer.getCurrentTime());283 this.scheduledPause = null;284 console.log("wavesurfer.getCurrentTime()1b: ", wavesurfer.getCurrentTime());285 this.startPosition += this.getPlayedTime();286 console.log("wavesurfer.getCurrentTime()1c: ", wavesurfer.getCurrentTime());287 this.source && this.source.stop(0);288 console.log("wavesurfer.getCurrentTime()1d: ", wavesurfer.getCurrentTime());289 this.setState(this.PAUSED_STATE);290 console.log("wavesurfer.getCurrentTime()1e: ", wavesurfer.getCurrentTime());291 },292 /**293 * Returns the current time in seconds relative to the audioclip's duration.294 */295 getCurrentTime: function () {296 return this.state.getCurrentTime.call(this);297 },298 /**299 * Set the audio source playback rate.300 */301 setPlaybackRate: function (value) {302 value = value || 1;303 if (this.isPaused()) {304 this.playbackRate = value;305 } else {306 this.pause();307 this.playbackRate = value;308 this.play();309 }310 }311};312WaveSurfer.WebAudio.state = {};313WaveSurfer.WebAudio.state.playing = {314 init: function () {315 },316 getPlayedPercents: function () {317 var duration = this.getDuration();318 return (this.getCurrentTime() / duration) || 0;319 },320 getCurrentTime: function () {321 return this.startPosition + this.getPlayedTime();322 }323};324WaveSurfer.WebAudio.state.paused = {325 init: function () {326 },327 getPlayedPercents: function () {328 var duration = this.getDuration();329 return (this.getCurrentTime() / duration) || 0;330 },331 getCurrentTime: function () {332 return this.startPosition;333 }334};335WaveSurfer.WebAudio.state.finished = {336 init: function () {337 this.fireEvent('finish');338 },339 getPlayedPercents: function () {340 return 1;341 },342 getCurrentTime: function () {343 return this.getDuration();...
BusinessHours.js
Source:BusinessHours.js
...63 <th scope='row'>Mon</th>64 <td>{getTime(props.hours.Mon)}</td>65 <td66 className={67 getCurrentTime('Mon', props.hours.Mon) === ''68 ? ''69 : getCurrentTime('Mon', props.hours.Mon) === 'Closed'70 ? classes.Closed71 : classes.Open72 }73 >74 {getCurrentTime('Mon', props.hours.Mon)}75 </td>76 </tr>77 <tr>78 <th scope='row'>Tue</th>79 <td>{getTime(props.hours.Tue)}</td>80 <td81 className={82 getCurrentTime('Tue', props.hours.Tue) === ''83 ? ''84 : getCurrentTime('Tue', props.hours.Tue) === 'Closed'85 ? classes.Closed86 : classes.Open87 }88 >{getCurrentTime('Tue', props.hours.Tue)}</td>89 </tr>90 <tr>91 <th scope='row'>Wed</th>92 <td>{getTime(props.hours.Wed)}</td>93 <td94 className={95 getCurrentTime('Wed', props.hours.Wed) === ''96 ? ''97 : getCurrentTime('Wed', props.hours.Wed) === 'Closed'98 ? classes.Closed99 : classes.Open100 }101 >{getCurrentTime('Wed', props.hours.Wed)}</td>102 </tr>103 <tr>104 <th scope='row'>Thu</th>105 <td>{getTime(props.hours.Thu)}</td>106 <td107 className={108 getCurrentTime('Thu', props.hours.Thu) === ''109 ? ''110 : getCurrentTime('Thu', props.hours.Thu) === 'Closed'111 ? classes.Closed112 : classes.Open113 }114 >{getCurrentTime('Thu', props.hours.Thu)}</td>115 </tr>116 <tr>117 <th scope='row'>Fri</th>118 <td>{getTime(props.hours.Fri)}</td>119 <td120 className={121 getCurrentTime('Fri', props.hours.Fri) === ''122 ? ''123 : getCurrentTime('Fri', props.hours.Fri) === 'Closed'124 ? classes.Closed125 : classes.Open126 }127 >{getCurrentTime('Fri', props.hours.Fri)}</td>128 </tr>129 <tr>130 <th scope='row'>Sat</th>131 <td>{getTime(props.hours.Sat)}</td>132 <td133 className={134 getCurrentTime('Sat', props.hours.Sat) === ''135 ? ''136 : getCurrentTime('Sat', props.hours.Sat) === 'Closed'137 ? classes.Closed138 : classes.Open139 }140 >{getCurrentTime('Sat', props.hours.Sat)}</td>141 </tr>142 <tr>143 <th scope='row'>Sun</th>144 <td>{getTime(props.hours.Sun)}</td>145 <td146 className={147 getCurrentTime('Sun', props.hours.Sun) === ''148 ? ''149 : getCurrentTime('Sun', props.hours.Sun) === 'Closed'150 ? classes.Closed151 : classes.Open152 }153 >{getCurrentTime('Sun', props.hours.Sun)}</td>154 </tr>155 </tbody>156 </table>157 ) : null}158 </div>159 );160}...
theming.js
Source:theming.js
...24 //Initialize the vars in the beginning so that you will always have access to them.25 var getCurrentTime = CONTACTS.util.getCurrentTime,26 theme,27 changeTheme;28 console.log(getCurrentTime() + " [js/theming.js] (document -> pagecreate) - start");29 // Dynamically set the theme based on the option selected in the popup.30 $('#theme-button-a').click(function() {31 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-a -> click) - start");32 theme = 'a';33 changeTheme();34 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-a -> click) - end");35 });36 // Dynamically set the theme based on the option selected in the popup.37 $('#theme-button-b').click(function() {38 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-b -> click) - start");39 theme = 'b';40 changeTheme();41 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-b -> click) - end");42 });43 // Dynamically set the theme based on the option selected in the popup.44 $('#theme-button-c').click(function() {45 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-c -> click) - start");46 theme = 'c';47 changeTheme();48 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-c -> click) - end");49 });50 // Dynamically set the theme based on the option selected in the popup.51 $('#theme-button-d').click(function() {52 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-d -> click) - start");53 theme = 'd';54 changeTheme();55 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-d -> click) - end");56 });57 // Dynamically set the theme based on the option selected in the popup.58 $('#theme-button-e').click(function() {59 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-e -> click) - start");60 theme = 'e';61 changeTheme();62 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-e -> click) - end");63 });64 // Dynamically set the theme based on the option selected in the popup.65 $('#theme-button-f').click(function() {66 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-f -> click) - start");67 theme = 'f';68 changeTheme();69 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-f -> click) - end");70 });71 // Dynamically set the theme based on the option selected in the popup.72 $('#theme-button-g').click(function() {73 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-g -> click) - start");74 theme = 'g';75 changeTheme();76 console.log(getCurrentTime() + " [js/theming.js] (#theme-button-g -> click) - end");77 });78 changeTheme = function() {79 console.log(getCurrentTime() + " [js/theming.js] (changeTheme) - theme = " + theme);80 // Keep the submit buttons in the alternate theme for contrast.81 if (theme !== 'a') {82 $('.ui-btn-b').removeClass('ui-btn-b').addClass('ui-btn-a');83 } else {84 $('.ui-btn-a').removeClass('ui-btn-a').addClass('ui-btn-b');85 }86 // Go through all the Pages and remove whatever they they currently have and apply the selected theme.87 $('[data-role="page"]').removeClass('ui-page-theme-a ui-page-theme-b ui-page-theme-c ui-page-theme-d ui-page-theme-e')88 .addClass('ui-page-theme-' + theme);89 }90 console.log(getCurrentTime() + " [js/theming.js] (document -> pagecreate) - end");...
First.jsx
Source:First.jsx
...5 var p2 = getWeatherFromOnet();6 var p3 = getWeatherFromGoogle();7 Promise.race([p1, p2, p3]).then(result => { console.log("can I go out now?", result) }).catch(result => { console.log("catch", result) });8 }9 function getCurrentTime(result, seconds, source, shouldFail) {10 return new Promise((resolve, reject) => {11 console.log(source + " before getCurrentTime");12 setTimeout(() => {13 if (shouldFail) {14 console.log(source + " after getCurrentTime FAIL");15 reject({ result, source });16 }17 else {18 console.log(source + " after getCurrentTime");19 resolve({ result, source });20 }21 }, 1000 * seconds)22 });23 }24 function getMyLocation(result, seconds, source, shouldFail) {25 return new Promise((resolve, reject) => {26 console.log(source + " before getMyLocation");27 setTimeout(() => {28 if (shouldFail) {29 console.log(source + " after getMyLocation FAIL");30 reject({ result, source });31 }32 else {33 console.log(source + " after getMyLocation");34 resolve({ result, source });35 }36 }, 1000 * seconds)37 });38 }39 function getWeatherFromMeteo() {40 return Promise.all([getCurrentTime(true, 3, "Meteo"), getMyLocation(true, 4, "Meteo")]);41 }42 function getWeatherFromOnet() {43 return Promise.all([getCurrentTime(true, 2, "Onet"), getMyLocation(true, 5, "Onet")]);44 }45 function getWeatherFromGoogle() {46 return Promise.all([getCurrentTime(true, 1, "Google", true), getMyLocation(true, 6, "Google", true)]);47 }48 return (49 <div>50 <button onClick={canIGoOutNow}>Click me!</button>51 </div>52 );53}...
datetime.js
Source:datetime.js
1function getCurrentTime() {2 const time = new Date();3 return time.toLocaleString();4}5// exports = module.exports // å³ï¼exports 对象æ¯æå module.exports çå¼ç¨6// exports.getCurrentTime = getCurrentTime; // 17// module.exports.getCurrentTime = getCurrentTime; // 2 ï¼ä¸1ç导åºæ¯çä»·ç 8// module.exports = getCurrentTime; // 3ï¼ç´æ¥å°getCurrentTimeèµå¼ç»module.exports对象9// ä¸é¢ä¸¤ç§çåºå«å¨requireçæ¶åï¼åºå«å¾ææ¾ï¼10// 1ï¼2两ç§å¯¼åºæ¹å¼ï¼éè¦è®¿é® getCurrentTime å±æ§è·åå° getCurrentTime å½æ°11// const myModule = require('myModule');12// myModule.getCurrentTime();13// 3 è¿ç§å¯¼åºæ¹å¼ï¼å¯ä»¥ç´æ¥ä½¿ç¨ getCurrentTime å½æ°14// const getCurrentTime = require('myModule')15// getCurrentTime();16// ç´æ¥åexports = getCurrentTime æ¯æ æ³å¯¼åº getCurrentTime å½æ°çï¼å 为exportsæ¬è´¨ä¸æ¯åmoduleç exportså±æ§çå¼ç¨ï¼ç´æ¥å¯¹exportsèµå¼åªä¼æ¹åexportsæ¬èº«ï¼è对module.exports并æ å½±åã...
Using AI Code Generation
1const { chromium } = require('playwright');2const { getCurrentTime } = require('playwright/lib/server/supplements/recorder/recorderSupplement');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 const time = getCurrentTime();8 console.log(time);9 await page.screenshot({ path: `example-${time}.png` });10 await browser.close();11})();12const { chromium } = require('playwright');13const { getCurrentTime } = require('playwright/lib/server/supplements/recorder/recorderSupplement');14(async () => {15 const browser = await chromium.launch();16 const context = await browser.newContext();17 const page = await context.newPage();18 const time = getCurrentTime();19 await page.screenshot({ path: `example-${time}.png` });20 await browser.close();21})();
Using AI Code Generation
1const { getCurrentTime } = require('playwright/lib/server/chromium/crNetworkManager');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 console.log(getCurrentTime());8 await browser.close();9})();
Using AI Code Generation
1const { chromium } = require('playwright');2const path = require('path');3(async () => {4 const browser = await chromium.launch({5 executablePath: path.join(__dirname, '..', 'node_modules', '.bin', 'chromium')6 });7 const context = await browser.newContext();8 const page = await context.newPage();9 await page.click('[placeholder="Search"]');10 await page.fill('[placeholder="Search"]', 'playwright');11 await page.press('[placeholder="Search"]', 'Enter');12 await page.click('text=Playwright');13 await page.click('text=Docs');14 await page.click('text=API');15 await page.click('text=Playwright');16 await page.click('text=Docs');17 await page.click('text=API');18 await page.click('text=Playwright');19 await page.click('text=Docs');20 await page.click('text=API');21 await page.click('text=Playwright');22 await page.click('text=Docs');23 await page.click('text=API');24 await page.click('text=Playwright');25 await page.click('text=Docs');26 await page.click('text=API');27 await page.click('text=Playwright');28 await page.click('text=Docs');29 await page.click('text=API');30 await page.click('text=Playwright');31 await page.click('text=Docs
Using AI Code Generation
1const { getCurrentTime } = require('playwright/lib/server/chromium/crNetworkManager');2const time = getCurrentTime();3console.log(time);4const { getCurrentTime } = require('playwright/lib/server/safari/sfNetworkManager');5const time = getCurrentTime();6console.log(time);7const { getCurrentTime } = require('playwright/lib/server/webkit/wkNetworkManager');8const time = getCurrentTime();9console.log(time);10const { getCurrentTime } = require('playwright/lib/server/firefox/ffNetworkManager');11const time = getCurrentTime();12console.log(time);13const { getCurrentTime } = require('playwright/lib/server/webkit/wkNetworkManager');14const time = getCurrentTime();15console.log(time);16const { getCurrentTime } = require('playwright/lib/server/firefox/ffNetworkManager');17const time = getCurrentTime();18console.log(time);19const { getCurrentTime } = require('playwright/lib/server/webkit/wkNetworkManager');20const time = getCurrentTime();21console.log(time);22const { getCurrentTime } = require('playwright/lib/server/firefox/ffNetworkManager');23const time = getCurrentTime();24console.log(time);25const { getCurrentTime } = require('playwright/lib/server/webkit/wkNetworkManager');26const time = getCurrentTime();27console.log(time);28const { getCurrentTime } = require('playwright/lib/server/firefox/ffNetworkManager');29const time = getCurrentTime();30console.log(time);31const { getCurrentTime } = require('playwright/lib/server/webkit/wkNetwork
Using AI Code Generation
1const { getCurrentTime } = require('@playwright/test/lib/utils/video/recorder');2const currentTime = getCurrentTime();3console.log(currentTime);4const { generateVideoName } = require('@playwright/test/lib/utils/video/recorder');5const videoName = generateVideoName();6console.log(videoName);7const { generateVideoPath } = require('@playwright/test/lib/utils/video/recorder');8const videoPath = generateVideoPath();9console.log(videoPath);10const { generateVideoPath } = require('@playwright/test/lib/utils/video/recorder');11const videoPath = generateVideoPath();12console.log(videoPath);13const { generateVideoPath } = require('@playwright/test/lib/utils/video/recorder');14const videoPath = generateVideoPath();15console.log(videoPath);16const { generateVideoPath } = require('@playwright/test/lib/utils/video/recorder');17const videoPath = generateVideoPath();18console.log(videoPath);19const { generateVideoPath } = require('@playwright/test/lib/utils/video/recorder');20const videoPath = generateVideoPath();21console.log(videoPath);22const { generateVideoPath } = require('@playwright/test/lib/utils/video/recorder');23const videoPath = generateVideoPath();24console.log(videoPath);25const { generateVideoPath } = require('@playwright/test/lib/utils/video/recorder');26const videoPath = generateVideoPath();27console.log(videoPath);28const { generateVideoPath } = require('@playwright/test/lib/utils/video/recorder');
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!