When I receive a webhook from Stripe saying that a subscription was created, I'm trying to update my database to give that user access to my subscription service. The webhook comes in saying that a subscription was created, so I take the UserID and use that to lookup the user in my database. Then I update the user record changing SubscriptionStatus to 1 indicating that it is active. When I try to save that information the database, it throws an exception and I receive an error from Stripe saying that the webhook failed with Error 400 (Bad Request). According to the response from Stripe, it says "The request could not be understood by the server due to malformed syntax."
This is the Response I received from Stripe:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>IIS 10.0 Detailed Error - 400.0 - Bad Request</title>
<style type="text/css">
<!--
body{margin:0;font-size:.7em;font-family:Verdana,Arial,Helvetica,sans-serif;}
code{margin:0;color:#006600;font-size:1.1em;font-weight:bold;}
.config_source code{font-size:.8em;color:#000000;}
pre{margin:0;font-size:1.4em;word-wrap:break-word;}
ul,ol{margin:10px 0 10px 5px;}
ul.first,ol.first{margin-top:5px;}
fieldset{padding:0 15px 10px 15px;word-break:break-all;}
.summary-container fieldset{padding-bottom:5px;margin-top:4px;}
legend.no-expand-all{padding:2px 15px 4px 10px;margin:0 0 0 -12px;}
legend{color:#333333;;margin:4px 0 8px -12px;_margin-top:0px;
font-weight:bold;font-size:1em;}
a:link,a:visited{color:#007EFF;font-weight:bold;}
a:hover{text-decoration:none;}
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;}
h3{font-size:1.4em;margin:10px 0 0 0;color:#CC0000;}
h4{font-size:1.2em;margin:10px 0 5px 0;
}#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS",Verdana,sans-serif;
color:#FFF;background-color:#5C87B2;
}#content{margin:0 0 0 2%;position:relative;}
.summary-container,.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
.content-container p{margin:0 0 10px 0;
}#details-left{width:35%;float:left;margin-right:2%;
}#details-right{width:63%;float:left;overflow:hidden;
}#server_version{width:96%;_height:1px;min-height:1px;margin:0 0 5px 0;padding:11px 2% 8px 2%;color:#FFFFFF;
background-color:#5A7FA5;border-bottom:1px solid #C1CFDD;border-top:1px solid #4A6C8E;font-weight:normal;
font-size:1em;color:#FFF;text-align:right;
}#server_version p{margin:5px 0;}
table{margin:4px 0 4px 0;width:100%;border:none;}
td,th{vertical-align:top;padding:3px 0;text-align:left;font-weight:normal;border:none;}
th{width:30%;text-align:right;padding-right:2%;font-weight:bold;}
thead th{background-color:#ebebeb;width:25%;
}#details-right th{width:20%;}
table tr.alt td,table tr.alt th{}
.highlight-code{color:#CC0000;font-weight:bold;font-style:italic;}
.clear{clear:both;}
.preferred{padding:0 5px 2px 5px;font-weight:normal;background:#006633;color:#FFF;font-size:.8em;}
-->
</style>
</head>
<body>
<div id="content">
<div class="content-container">
<h3>HTTP Error 400.0 - Bad Request</h3>
<h4>Bad Request</h4>
</div>
<div class="content-container">
<fieldset><h4>Most likely causes:</h4>
<ul> <li></li> </ul>
</fieldset>
</div>
<div class="content-container">
<fieldset><h4>Things you can try:</h4>
<ul> <li>Check the failed request tracing logs for additional information about this error. For more information, click here. </li> </ul>
</fieldset>
</div>
<div class="content-container">
<fieldset><h4>Detailed Error Information:</h4>
<div id="details-left">
<table border="0" cellpadding="0" cellspacing="0">
<tr class="alt"><th>Module</th><td> ManagedPipelineHandler</td></tr>
<tr><th>Notification</th><td> ExecuteRequestHandler</td></tr>
<tr class="alt"><th>Handler</th><td> System.Web.Mvc.MvcHandler</td></tr>
<tr><th>Error Code</th><td> 0x00000000</td></tr>
</table>
</div>
<div id="details-right">
<table border="0" cellpadding="0" cellspacing="0">
<tr class="alt"><th>Requested URL</th><td> https://localhost:44394/Stripe/StripeWebhook</td></tr>
<tr><th>Physical Path</th><td> C:\Users\steve\source\repos\Church Musician Administration App\Church Musician Administration App (Updated)\Stripe\StripeWebhook</td></tr>
<tr class="alt"><th>Logon Method</th><td> Anonymous</td></tr>
<tr><th>Logon User</th><td> Anonymous</td></tr>
</table>
<div class="clear"></div>
</div>
</fieldset>
</div>
<div class="content-container">
<fieldset><h4>More Information:</h4>
The request could not be understood by the server due to malformed syntax.
<p>View more information ยป</p>
<p>Microsoft Knowledge Base Articles:</p>
<ul><li></li></ul>
</fieldset>
</div>
</div>
</body>
</html>
This is my Webhook Endpoint in my app:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Database_Access;
using Stripe;
namespace Church_Musician_Administration_App__Updated_.Controllers
{
public class StripeController : Controller
{
private churchmusicianEntities db = new churchmusicianEntities();
[AllowAnonymous]
[HttpPost]
public ActionResult StripeWebhook()
{
try
{
var json = new StreamReader(HttpContext.Request.InputStream).ReadToEnd();
// validate webhook called by stripe only
var stripeEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], "whsec_jLsw8GxNmEKkcyxhjNeDTpJvI5xhggXu");
switch (stripeEvent.Type)
{
case "customer.created":
var customer = stripeEvent.Data.Object as Customer;
// do work
break;
case "customer.subscription.created":
var subscriptionCreated = stripeEvent.Data.Object as Subscription;
string subscriptionCustID = subscriptionCreated.CustomerId;
var subscriptionCustomers = db.logins.Where(x => x.StripeCustomerID == subscriptionCustID);
foreach (var subscriptionCustomer in subscriptionCustomers)
{
if (subscriptionCreated.Status == "trialing" || subscriptionCreated.Status == "active")
{
subscriptionCustomer.SubscriptionStatus = 1;
db.Entry(subscriptionCustomer).State = EntityState.Modified;
db.SaveChanges();
}
}
break;
case "customer.subscription.updated":
case "customer.subscription.deleted":
case "customer.subscription.trial_will_end":
var subscription = stripeEvent.Data.Object as Subscription;
// do work
break;
case "invoice.created":
var newinvoice = stripeEvent.Data.Object as Invoice;
// do work
break;
case "invoice.upcoming":
case "invoice.payment_succeeded":
var successPayment = stripeEvent.Data.Object as Invoice;
string customerID = successPayment.CustomerId;
var custs = db.logins.Where(x => x.StripeCustomerID == customerID);
foreach (var cust in custs)
{
cust.SubscriptionExpiration = successPayment.PeriodEnd.ToString();
db.SaveChanges();
}
break;
case "invoice.payment_failed":
var changeSubscription = stripeEvent.Data.Object as Invoice;
string customerID2 = changeSubscription.CustomerId;
if(changeSubscription.Status != "active")
{
var payingCustomers = db.logins.Where(x => x.StripeCustomerID == customerID2);
foreach (var payingCustomer in payingCustomers)
{
payingCustomer.SubscriptionStatus = 0;
db.SaveChanges();
}
}
// do work
break;
case "coupon.created":
case "coupon.updated":
case "coupon.deleted":
var coupon = stripeEvent.Data.Object as Coupon;
// do work
break;
// DO SAME FOR OTHER EVENTS
}
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
catch (StripeException ex)
{
//_logger.LogError(ex, $"StripWebhook: {ex.Message}");
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
catch (Exception ex)
{
//_logger.LogError(ex, $"StripWebhook: {ex.Message}");
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}
}
}
The problem is likely due to something going wrong in your database logic, which is causing your server to return the full error to Stripe, which Stripe cannot read. Instead you should return a 200 early to indicate that the webhook event was received, and then handle your business logic: https://stripe.com/docs/webhooks/build#return-a-2xx-status-code-quickly
Related
I am writing a dropdown component in svelte with tailwind css and i cannot get the dropdown menu to be under the button/anchored to it meaning it always appears under it.
I have tried using relative and other positional tailwind properties but i haven't had any succes.
I am also new to tailwind and mainly have backend experience so this is a bit hard for me if anyone can help or point me towards an example then please do so!
Dropdown example in Svelte
https://svelte.dev/repl/05d770c4baa748b2b0973afec4d59051?version=3.55.1
<script>
// Button binding
let button;
// State
let open = false;
let top = 0, left = 0;
// React to opening
$: if (open) {
setDropdownPosition();
}
function setDropdownPosition() {
const rect = button.getBoundingClientRect();
top = rect.bottom;
left = rect.left;
}
</script>
<button bind:this={button} on:click={() => open = !open}>{!open ? 'Open' : 'Close'} Dropdown!</button>
{#if open}
<ul class="dropdown" style:top={top} style:left={left}>
<li>Option 1</li>
<li>Option 2</li>
<li>Option 3</li>
</ul>
{/if}
<style>
/* List Reset */
ul li {
padding: 0;
text-indent: 0;
list-style-type: none;
}
.dropdown {
position: absolute;
display: grid;
gap: 0.25rem;
margin: 0.5rem 0 0 0.5rem;
padding: 0;
}
</style>
I am trying to add Stripe Subscription to my Blazor WASM Application following these instructions Since they are using JavaScript I am using the JavaScript interop. I added Stripe's script to my index.html and added a custom script with the javascript they have in the instructions.
Index.html inside the <head> tag
<script src="https://js.stripe.com/v3/"></script>
<script src="stripescript.js"></script>
stripescript.js:
let stripe = window.Stripe('MY PUBLIC KEY');
let elements = stripe.elements();
let card = elements.create('card', { style: style });
card.mount('#card-element');
card.on('change', function (event) {
displayError(event);
});
function displayError(event) {
changeLoadingStatePrices(false);
let displayError = document.getElementById('card-element-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
}
function createPaymentMethod(cardElement, customerId, priceId) {
return stripe
.createPaymentMethod({
type: 'card',
card: cardElement,
})
.then((result) => {
if (result.error) {
displayError(error);
} else {
//change this to call .net
createSubscription({
customerId: customerId,
paymentMethodId: result.paymentMethod.id,
priceId: priceId,
});
}
});
}
My assumption is that the variable initializations would happen when the application is loaded. However, when I add the following HTML to my Razor page is not populating the card component.
<form id="payment-form">
<div id="card-element">
<!-- Elements will create input elements here -->
</div>
<!-- We'll put the error messages in this element -->
<div id="card-element-errors" role="alert"></div>
<button type="submit">Subscribe</button>
</form>
I am lost on how to debug this, or if this is even possible in Blazor.
Thanks to #Umair's Comments, I realized that I had made a few mistakes and some of them were showing up in the console since I was trying to initialize the card element before the DOM was loaded. I was able to fix my problem by first changing the card mount into its own function. Here is the full stripescript.js for future people that have this problem:
let stripe = window.Stripe('MY KEY');
let elements = stripe.elements();
let style = {
base: {
fontSize: '16px',
color: '#32325d',
fontFamily:
'-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif',
fontSmoothing: 'antialiased',
'::placeholder': {
color: '#a0aec0',
},
},
};
let card = elements.create('card', { style: style });
function mountCard() {
card.mount('#card-element');
}
card.on('change', function (event) {
displayError(event);
});
function displayError(event) {
changeLoadingStatePrices(false);
let displayError = document.getElementById('card-element-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
}
function createPaymentMethod(cardElement, customerId, priceId) {
return stripe
.createPaymentMethod({
type: 'card',
card: cardElement,
})
.then((result) => {
if (result.error) {
displayError(error);
} else {
//todo change this to call .net
createSubscription({
customerId: customerId,
paymentMethodId: result.paymentMethod.id,
priceId: priceId,
});
}
});
}
and added the following C# code to my Blazor component to render the card:
[Inject] IJSRuntime js { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await js.InvokeVoidAsync("mountCard");
}
I found a solution for this on Youtube that is super complicated, and I decided to create my own implementation following KISS.
I have streamlined youtube's implementation on a single StripeCard component that interacts with my custom Js StripeInterop. I hope this makes your life easier.
This will allow you to have different publishable keys for your different environments without hardcoding it and reuse the component in multiple pages if you wish. Also, it will destroy itself when the component is used.
Here is my solution for (blazor webassembly).
Add this to index.html
<!-- Stripe -->
<script src="https://js.stripe.com/v3/"></script>
<script src="stripescript.js"></script>
Here is stripescript.js
StripeInterop = (() => {
var stripe = null;
var elements = null;
var dotNetReference = null;
var card = null;
var style = {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
return {
init(dotnetHelper, publishableKey) {
stripe = window.Stripe(publishableKey);
elements = stripe.elements();
card = elements.create('card', { style: style });
dotNetReference = dotnetHelper;
card.mount('#card-element');
card.on('change', function (event) {
displayError(event);
});
},
createPaymentMethod(billingEmail, billingName) {
return stripe
.createPaymentMethod({
type: 'card',
card: card,
billing_details: {
name: billingName,
email: billingEmail
}
})
.then(function (result) {
if (result.error) {
displayError(result);
} else {
dotNetReference.invokeMethodAsync('ProcessPaymentMethod', result.paymentMethod.id);
}
});
},
destroy() {
dotNetReference.dispose();
card.destroy();
}
};
function displayError(event) {
var showError = document.getElementById('card-element-errors');
if (event.error) {
showError.textContent = event.error.message;
} else {
showError.textContent = '';
}
}
})();
Here is StripeCard.razor component
#namespace SmartApp.Components
<div id="card-element" style="display: block;
width: 100%;
padding: 0.52rem .75rem;
font-size: 1rem;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: .25rem;
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;">
</div>
<div id="card-element-errors" class="validation-message"></div>
Here is StripeCard.razor.cs
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace SmartApp.Components
{
public partial class StripeCard : IDisposable
{
[Inject] IJSRuntime JS { get; set; }
[Parameter] public string PublishableKey { get; set; }
[Parameter] public EventCallback<string> CardProcessedCallBack { get; set; }
private bool _firstTime;
protected override async Task OnInitializedAsync()
{
_firstTime = true;
await base.OnInitializedAsync();
}
public async void Dispose()
{
await JS.InvokeVoidAsync("StripeInterop.destroy");
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (_firstTime)
{
_firstTime = false;
await JS.InvokeVoidAsync("StripeInterop.init", DotNetObjectReference.Create(this), PublishableKey);
}
}
[JSInvokable("ProcessPaymentMethod")]
public Task ProcessPaymentMethod(string paymentId)
{
return CardProcessedCallBack.InvokeAsync(paymentId);
}
}
}
Here is how we use StripeCard.razor in our Page.
#page "/subscription/payment"
#attribute [Authorize(Roles = "Admin,Organisation")]
#inject IJSRuntime JS
<div class="card card-custom card-shadowless rounded-top-0">
<div class="card-body p-0">
<div class="row justify-content-center py-8 px-8 py-lg-15 px-lg-10">
<div class="col-xl-12 col-xxl-7">
<!--begin: Wizard Form-->
<EditForm Model="#_model" OnValidSubmit="HandleValidSubmit" >
<DataAnnotationsValidator />
<h4 class="mb-10 font-weight-bold text-dark">Enter your Payment Details</h4>
<div class="row">
<div class="col-xl-6">
<!--begin::Input-->
<div class="form-group">
<label>Name on Card</label>
<InputText #bind-Value="_model.BillingName" name="ccname" class="form-control form-control-solid form-control-lg"
placeholder="Jane Doe" />
<ValidationMessage For="#(() => _model.BillingName)" />
</div>
<!--end::Input-->
</div>
<div class="col-xl-6">
<!--begin::Input-->
<div class="form-group">
<label>Notification Email</label>
<InputText #bind-Value="_model.BillingEmail" name="ccemail" class="form-control form-control-solid form-control-lg"
placeholder="jane.doe#domain.com" />
<ValidationMessage For="#(() => _model.BillingEmail)" />
</div>
<!--end::Input-->
</div>
</div>
<div class="row">
<div class="col-xl-12">
<!--begin::Input-->
<div class="form-group">
<label>Card Information</label>
<StripeCard PublishableKey="#_stripePublishableKey" CardProcessedCallBack="ProcessSubscriptionAsync"></StripeCard>
<span class="form-text text-muted">Powered by <strong>Stripe</strong>.</span>
</div>
<!--end::Input-->
</div>
</div>
<ValidationSummary />
<div class="d-flex justify-content-between border-top mt-5 pt-10">
<div>
<button type="button" class="btn btn-success font-weight-bolder text-uppercase px-9 py-4"
#onclick="HandleValidSubmit">
Submit
</button>
</div>
</div>
</EditForm>
<!--end: Wizard Form-->
</div>
</div>
</div>
</div>
#code{
private Model _model;
private string _stripePublishableKey;
protected override void OnInitialized()
{
_model = new Model();
_stripePublishableKey = "Here we put Development or Production publishableKey"; //Add publishable key here
base.OnInitialized();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync("KTSubscriptionCheckout.init");
}
await base.OnAfterRenderAsync(firstRender);
}
private async Task HandleValidSubmit()
{
await JS.InvokeVoidAsync("StripeInterop.createPaymentMethod", _model.BillingEmail, _model.BillingName);
}
//Callback method will return stripe paymentId
private async Task ProcessSubscriptionAsync(string paymentId)
{
//We process paymentId here and continue with our backend process
await Task.CompletedTask;
}
public class Model
{
[Required]
public string BillingName { get; set; }
[Required]
public string BillingEmail { get; set; }
}
}
Youtube video if you want to have a look. https://youtu.be/ANYvFHHfyy8
Good luck...
I'm having a problem with my hybrid iOS application, in iOS 11 beta. It is basically a web app running within a WKWebView. The problem does not exist in iOS 10, but has been introduced with iOS 11. As of beta 8, the problem is still reproducible.
When the webapp calls the JavaScript function scrollIntoView a couple of times, parts of the GUI becomes unresponsive.
The following reduced HTML file demonstrates the problem:
<!DOCTYPE html>
<html><head>
<meta charset="UTF-8">
<meta id="viewportMeta" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<title>Testcase</title>
<script>
let numClicks = 0;
function runScrollIntoView() {
document.querySelector(".element-1").scrollIntoView();
}
function debugInfo() {
return {
"window.screen.height": window.screen.height,
"window.screen.availHeight": window.screen.availHeight,
"document.body.scrollTop": document.body.scrollTop,
"document.body.scrollHeight": document.body.scrollHeight
};
}
function clickHandler() {
numClicks++;
document.querySelector('.counter').innerText += numClicks + " clicks, " + JSON.stringify(debugInfo()) + "\n";
}
</script>
<style>
.instructions div {
border: 1px solid blue;
color: blue;
}
.counter {
width: 300px;
height: 300px;
border: 1px solid black;
}
</style>
</head>
<body>
<div class="instructions">
<div onclick="clickHandler()">1. Click me to run click handler</div>
<div onclick="runScrollIntoView()">2. Click me to run Element.scrollIntoView</div>
<div onclick="clickHandler()">3. Click me to run click handler again</div>
<div onclick="runScrollIntoView()">4. Click me to run Element.scrollIntoView again</div>
<div onclick="clickHandler()">5. Click me to try run click handler again - nothing happens</div>
<div>(Repeat 4-5 if necessary)</div>
</div>
<div class="counter">
Number of clicks
</div>
<div class="element-1">Target to scrollIntoView</div>
</body>
</html>
... when being loaded with this viewcontroller:
import UIKit
import WebKit
import Foundation
import SystemConfiguration
class ViewControllerNgf: UIViewController, WKNavigationDelegate, WKUIDelegate {
var webView: WKWebView!
override func loadView() {
self.webView = WKWebView(frame: CGRect.zero)
webView.navigationDelegate = self
webView.uiDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
self.loadWebApp()
}
fileprivate func loadWebApp() {
let htmlFile = Bundle.main.path(forResource: "htmlfile", ofType: "html")
let html = try? String(contentsOfFile: htmlFile!, encoding: String.Encoding.utf8)
DispatchQueue.main.async {
self.webView!.loadHTMLString(html!, baseURL: nil)
}
}
}
I have reported this as a bug to Apple, but no response yet, leading me to think I'm doing something wrong in the app. But the Swift code is basically copied from Apple's documentation.
So what could be the problem?
The problem seems to have been resolved in iOS 11.2.
I'm having problems with my data driven coded-ui test. I'm supposed to replace the info typed in the test with csv data.
I can succesfully replace login and password inputs with the csv data.. but I'm failing to replace a selected LI in the test with it's csv data.
I don't know how to select the LI since it seems to be a custom generated control element but I may be drifting.
The HTML looks like this:
<div id="cphCont_cphContAut_ddlTipoJur" class="ComboAzul" style="display:inline-block;">
<table id="cphCont_cphContAut_ddlTipoJur_ddlTipoJur_Table" class="ajax__combobox_inputcontainer" style="border-width:0px;border-style:None;border-collapse:collapse;display:inline-block;position:relative;top:5px;" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td class="ajax__combobox_textboxcontainer">
<input name="ctl00$ctl00$cphCont$cphContAut$ddlTipoJur$ddlTipoJur_TextBox" id="cphCont_cphContAut_ddlTipoJur_ddlTipoJur_TextBox" tabindex="3" autocomplete="off" style="width: 260px; margin: 0px;" type="text">
</td>
<td class="ajax__combobox_buttoncontainer">
<button style="height: 18px; width: 18px; margin: 0px; padding: 0px; visibility: visible;" id="cphCont_cphContAut_ddlTipoJur_ddlTipoJur_Button" type="button"></button>
</td>
</tr>
</tbody>
</table>
<ul id="cphCont_cphContAut_ddlTipoJur_ddlTipoJur_OptionList" class="ajax__combobox_itemlist" style="visibility: hidden; z-index: 1000; overflow: hidden; width: 310px; position: absolute; height: 133px; left: 638px; top: 425px; display: none;">
<li style=""> </li>
<li style="">Cons / Ou</li>
<li style="">Coop</li>
<li style="">Empr Ind</li>
<li style="">Req E</li>
<li>Soc A</li>
<li>Soc E</li>
</ul>
<input name="ctl00$ctl00$cphCont$cphContAut$ddlTipoJur$ddlTipoJur_HiddenField" id="cphCont_cphContAut_ddlTipoJur_ddlTipoJur_HiddenField" value="0" type="hidden">
</div>
In the UIMap.Designer.cs the UL ( I believe ) look like this:
[GeneratedCode("Coded UITest Builder", "12.0.21005.1")]
public class UICphCont_cphConteCustom : HtmlCustom
{
public UICphCont_cphContCustom(UITestControl searchLimitContainer) :
base(searchLimitContainer)
{
#region Search Criteria
this.SearchProperties["TagName"] = "UL";
this.SearchProperties["Id"] = "cphCont_cphContAut_ddlTipoJur_ddlTipoJur_OptionList";
this.SearchProperties[UITestControl.PropertyNames.Name] = null;
this.FilterProperties["Class"] = "ajax__combobox_itemlist";
this.FilterProperties["ControlDefinition"] = "class=\"ajax__combobox_itemlist\" id=\"cphC";
this.FilterProperties["TagInstance"] = "1";
this.WindowTitles.Add("Test");
#endregion
}
Selected LI element looks like this:
public HtmlCustom UISocaCustom
{
get
{
if ((this.mUISocaCustom == null))
{
this.mUISocaCustom = new HtmlCustom(this);
#region Search Criteria
this.mUISocaCustom.SearchProperties["TagName"] = "LI";
this.mUISocaCustom.SearchProperties["Id"] = null;
this.mUISocaCustom.SearchProperties[UITestControl.PropertyNames.Name] = null;
this.mUISocaCustom.FilterProperties["Class"] = null;
this.mUISocaCustom.FilterProperties["ControlDefinition"] = null;
this.mUISocaCustom.FilterProperties["InnerText"] = "Soc A";
this.mUISocaCustom.FilterProperties["TagInstance"] = "6";
this.mUISocaCustom.WindowTitles.Add("tEST");
#endregion
}
return this.mUISocaCustom;
}
}
The code I'm using to replace the login and password with the csv data is :
public void MetdG()
{
this.UIMap.TesterParams.UITxtLoginEditText = TestContext.DataRow["login"].ToString();
this.UIMap.TesterParams.UITxtSenhaEditPassword = Playback.EncryptText(TestContext.DataRow["senha"].ToString());
this.UIMap.Tester();
// To generate code for this test, select "Generate Code for Coded UI Test" from the shortcut menu and select one of the menu items.
}
Any idea on how to proceed ?
Edit : As instructed by Ryan Cox
It was created a static class to hold the browser window :
[CodedUITest]
public static class GlobalVars
{
public static BrowserWindow myWindow;
}
Test Initializer method was edited to match this change :
[TestInitialize()]
public void BrowserStarter()
{
GlobalVars.myWindow = BrowserWindow.Launch(new Uri("www.google.com"));
}
MainG method was edited so the window could execute the script :
GlobalVars.myWindow.ExecuteScript("var xpath = li[text()='Soca'];var matchingElement = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;);matchingElement.innerHTML = arguments[0];", TestContext.DataRow["type"].ToString());
Now the problem is that the script is throwing an exception. I believe it's because it's searching for the element before it exists ( The test goes thru 2 or 3 pages before this point )
What you're trying to do is change the inner html on a list item, not enter text as you would in your <input> elements, so you'll need to execute a script to do this and modify the DOM. Since it's not a normal action for a user (click on this, enter text there, expand this combo box, etc.), you'll have to use javascript to complete it.
browserWindow.ExecuteScript("var elements = document.getElementsByTagName('li')
for (var i = 0; i < elements.length; i++) {
if (elements[i].innerHTML.indexOf("Soc A") !== -1) {
elements[i].innerHTML = arguments[0];
break;
}
}", TestContext.DataRow["type"].ToString());
//Comment to fill space
Of course, if you had an id on your list items, this would be even easier:
browserWindow.ExecuteScript("document.getElementById("Soc A list item").innerHTML = arguments[0];", TestContext.DataRow["type"].ToString());
Major props to Ryan Cox for all the help he provided yesterday !
The issue got solved with by adding this line of code :
this.UIMap.TestWindow.TestDocument5.UIContCustom.UISocaCustom.
SearchProperties[HtmlCustom.PropertyNames.InnerText] =
TestContext.DataRow["type"].ToString();
Being TestDocument5 the 5th page in the test ( as intended ), UIContCustom the UL element and UISocaCustom the custom HTML element the test generated.
It is worth noting that trying to change the value of InnerText through GetProperty will fail since it will spit out a read-only message.
I am using parsed console log plugin and Email-ext plugin in Jenkins to send out daily build status, only upon build failure or compiler warnings. I would like to display the extracted error/warning message in the email body. I got groovy email template from "https://github.com/jenkinsci/email-ext-plugin/blob/master/src/main/resources/hudson/plugins/emailext/templates/groovy-html.template". It display Console Output instead of specific error/warning message want.
I have zero knowledge on groovy or html et al, it gonna take me sometime to learn and able to modify the template to fulfill my need quickly.
Can someone point me a sample file that can search out either console output or parsed console output and only display the lines contain "error" or "warning"?
Any help is greatly appreciated.
Unfortunately, much of the HTML here is quite ugly to be compatible with Outlook limited HTML support.
<%
def paddingForDepth(int depth)
{
return "padding-left:${depth * 30}px";
}
def insertErrorPaneRow(int depth, Closure contents)
{
%>
<tr class="${tableLineClass()}">
<td class="icon_cell"></td>
<td class="console_cell"></td>
<td class="phase_name_cell" style="${paddingForDepth(depth)}">
<table width="100%" class="errorsPane">
<%
contents()
%>
</table>
</td>
</tr>
<%
}
def insertConsoleSummary(def build, int depth)
{
if (build.result != hudson.model.Result.FAILURE)
return;
final BeforeSummary = 0
final SummaryStarted = 1
final SummaryEnded = 2
BufferedReader logReader = new BufferedReader(build.getLogReader());
List<String> errorLines = new LinkedList<String>();
List<String> errorSummary = new LinkedList<String>();
Boolean msBuildDetected = false;
int scanStage = BeforeSummary;
try
{
for (String line = logReader.readLine(); line != null; line = logReader.readLine())
{
if (line.contains(' error ') || line.contains(' warning '))
errorLines.add(line);
if (line.contains('Microsoft (R) Build Engine version '))
msBuildDetected = true;
if (msBuildDetected)
{
switch (scanStage)
{
case BeforeSummary:
if (line.equals('Build FAILED.') || line.equals('Build succeeded.'))
scanStage = SummaryStarted;
if (line.equals('Attempting to cancel the build...'))
{
scanStage = SummaryEnded;
msBuildDetected = false;
}
break;
case SummaryStarted:
if (line ==~ /^\s*\d+ Warning\(s\)/)
scanStage = SummaryEnded;
else
errorSummary.add(line);
break;
}
}
}
}
finally
{
logReader.close();
}
if ((msBuildDetected && (errorSummary.size() > 0)) || (errorLines.size() > 0))
{
insertErrorPaneRow(depth) {
%><tr><td><pre><%
if (msBuildDetected)
errorSummary.each { l -> println l }
else
errorLines.each { l -> println l }
%></pre></td></tr><%
}
}
}
%>
<STYLE>
.icon_cell { padding: 3px; padding-left: 5px; padding-right: 5px; height:16px; vertical-align:middle; }
.console_cell { padding: 3px; padding-left: 5px; padding-right: 15px; height:16px; vertical-align:middle; }
.phase_name_cell { height:16px; vertical-align:middle; }
.errorsPane { background-color:#ffe0e0; }
</STYLE>
<BODY>
<!-- CONSOLE OUTPUT -->
<TABLE width="100%">
<TR><TD class="bg1"><B>BUILD SUMMARY</B></TD></TR>
</TABLE>
<BR/>
<table border="0" class="phasesTable"><tbody>
<%
insertConsoleSummary(build, 0);
%>
</tbody></table>
<BR/>
</BODY>