I am currently trying to use playwright to measure network performance, especially network consumption for a typical user session.
E.G. navigating from page to page, filling some form, ...
I have implemented an interceptor in angular as below:
#Injectable()
export class MetricInterceptor implements HttpInterceptor {
constructor(
private readonly metricService: MetricService,
) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.metricService.addHttpRequest(); // adding approximated request header size that I observed
if (req.body && req.body.contentLength) {
this.metricService.addAscendingBytes(+req.body.contentLength);
}
return next.handle(req).pipe(
tap(event => {
this.metricService.addHttpResponse(); // adding approximated response header size that I observed
if (event.headers && event.headers.get('content-length')) {
const contentLength = +event.headers.get('content-length') ?? 0;
this.metricService.addDescendingBytes(contentLength);
}
}),
);
}
}
Then I display values on pages, and get them in playwright to measure network use over a session
After trying to find some docs on how to do that, I didn't find a way to do it in a better way. Especially could I in playwright get access to chrome dev tools and extract below values
Related
I'm trying to write test cases around a component that contains my FullCalendar component in Angular.
I've written karma tests using a MockFullCalendar that extends FullCalendar and have been successfully able to mock the getApi().view calls for getting fake active start and active end dates. But when I use the same method for getApi().getResources() and getApi.getResourceById(id) I get this error when running tests
error TS2339: Property 'getResourceById' does not exist on type 'Calendar'.
const calendarResource = this.calendar.getApi().getResourceById(resourceIdAssignedToEvent);
Here are my mock classes
export class MockFullCalendarComponent extends FullCalendarComponent {
getApi(): any {
return {};
}
}
export class MockCalendarApi extends CalendarApi {
getResources(): any {
return [];
}
getResourceById(resourceId: any): any {
return {};
}
}
export class MockCalendar extends MockCalendarApi {
get view(): any {
return {
get activeStart(): any {
return new Date();
},
get activeEnd(): any {
return new Date();
}
};
}
}
The following is how I was able to successfully mock the getApi().view
export class MockFullCalendarComponent extends FullCalendarComponent {
getApi(): any {
return {
view: {
activeStart: new Date(),
activeEnd: new Date()
}
};
}
}
Using the same pattern somehow does not work for the Calendar and CalendarApi classes. I'm not sure if this is because there is a class and an interface of the same name (CalendarApi) in FullCalendar common and only the interface contains these two methods.
I'm using Angular 12 and FullCalendar V5.
I've tried mocking as described above, tried different ways to provide my mock classes to the TestBed for injection.
My test case does not run at all, because these methods aren't detected somehow, none of the existing tests run at all, its almost like the whole initialization of the test case fails at this and I'm not sure how else I can force the spec to use my mock classes that contains mock implementations of those methods.
I want to enable my users to set certain global colors when using the app. Therefor I have created a 'dynamicVariables.css' file:
:root {
--my-color: violet;
}
It is imported in 'global.scss' file:
#import "./theme/dynamicVariables.css";
Also, I've added a colorpicker on one page and I can set the --my-color variable fine from there.
onColorChange(data: any) {
document.documentElement.style.setProperty('--my-color', data);
}
Just when closing the app on my device (I've deployed it with ionic capacitor run android), it resets the css variable, because when I run it again the color is back to its default value.
I'm pretty sure, I have a general misconception here and would be grateful for some clarification. I'm generally new to web development and would be grateful for any help.
Thanks in advance.
just like how Mustafa explained in comments, you need to make these changes outside app "runtime" and in the device's memory, that would stay there even after the app (whether web or native) is closed. for example you can use ionic storage and save your data with keys and values same as your css keys, and load it up whenever the app opens.
Thanks to the responds, I was able to solve the problem with the help of Ionic Storage.
First, I created a Storage Service:
import { Storage } from '#ionic/storage-angular';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class StorageService {
private _storage: Storage | null = null;
constructor(private storage: Storage) {
}
async init() {
const storage = await this.storage.create();
this._storage = storage;
}
public set(key: string, value: any) {
this._storage?.set(key, value);
}
public get(key: string) {
return this._storage?.get(key);
}
}
When starting the app, I run the following code in the app.component.ts
async ngOnInit() {
await this.storageService.init();
let storedPathologicalColor = await this.storageService.get('--my-color');
if (storedPathologicalColor == null)
document.documentElement.style.setProperty('--my-color', getComputedStyle(document.documentElement).getPropertyValue('--my-color'))
else
document.documentElement.style.setProperty('--my-color', storedPathologicalColor);
}
It is important to init() the service from outside. When setting a new css variable, I also set a new key/value pair to the Storage.
Thanks again.
I am trying to solve an issue whereby I need to combine third party events with the eventBus send and reply approach that Vertx provides for Standard and Worker Verticle setups. I am not sure if what I have laid out below is necessarily the correct approach.
Problem Statement:
I want to have standard verticle that sends a message to a worker verticle.
The worker verticle does some preprocessing and then uses a client method provided by a third party state management lib to publish an even (in an async manner). The result of which is only whether or not the event was successfully received or not (but does not contain any further info around processing etc).
Further processing takes place when the third party state management lib receives the event(this all happens on a separate thread) and a success or failure can occur at which point another event will be published to the cluster management tools output channel.
From the output channel listener I then want to be able to use the event to somehow use the message.reply() on the worker verticle to send back a response to the standard verticle that made the original request, thereby closing the loop of the entire request lifecycle but also using the async approach that vertx is built to use.
Now I conceptually know how to do 90% of what is described here but the missing piece for me is how to coordinate the event on the output channel listener and connect this to the worker verticle so that I can trigger the message.reply.
I have looked at possibly using SharedData and Clustering that Vertx has but was wondering if there is possibly another approach.
I have put a possible example implementation but would really appreciate if anyone has any insights/thoughts into how this can be accomplished and if I am on the right track.
class Order(val id: String)
class OrderCommand(val order: Order) : Serializable {
companion object {
const val name = "CreateOrderCommand"
}
}
class SuccessOrderEvent(val id: String) : Serializable {
companion object {
const val name = "OrderSuccessfulEvent"
}
}
interface StateManagementLib {
fun <T> send(
value: T,
success: Handler<AsyncResult<Int>>,
failure: Handler<AsyncResult<Exception>>
) {
val output = publish(value)
if (output == 1) {
success.handle(Future.succeededFuture())
} else {
failure.handle(Future.failedFuture("Failed"))
}
}
// non-blocking
fun <T> publish(value: T): Int // returns success/failure only
}
class WorkVerticle constructor(private val lib: StateManagementLib) : AbstractVerticle() {
override fun start(startPromise: Promise<Void>) {
workerHandler()
startPromise.complete()
}
private fun workerHandler() {
val consumer = vertx.eventBus().consumer<OrderCommand>(OrderCommand.name)
consumer.handler { message: Message<OrderCommand> ->
try {
vertx.sharedData().getClusterWideMap<String, Message<OrderCommand>>("OrderRequest") { mapIt ->
if (mapIt.succeeded()) {
lib.send(message.body(), {
// The StateManagementLib successfully propagated the event so, we try and store in this map (id -> Message)
mapIt.result().put(message.body().order.id, message)
}, {
message.fail(400, it.result().message)
})
}
}
} catch (e: Exception) {
message.fail(
HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), "Failed to encode data."
)
}
}
// A consumer that will pick up an event that is received from the clusterTool output channel
vertx.eventBus().addInboundInterceptor { context: DeliveryContext<SuccessOrderEvent> ->
// Retrieve cluster map to get the previously stored message and try to respond with Message.reply
// This should go back to the Standard Verticle that sent the message
vertx.sharedData().getClusterWideMap<String, Message<OrderCommand>>("OrderRequest") {
if (it.succeeded()) {
val id = context.message().body().id
val mapResult = it.result().get(id)
it.result().remove(id)
// Try and reply so the original eventloop thread can pickup and respond to calling client
mapResult.result().reply(id)
}
}
}
}
}
I'm trying to configure Actuator's health probes to include checks for an external service that is nested beyond the first level. For example, when calling /actuator/health, these are the available health indicators:
{
"status":"DOWN",
"components":{
"jms":{
"status":"DOWN",
"components":{
"broker1":{
"status":"DOWN",
"details":{
"error":"javax.jms.JMSException: Failed to create session factory"
}
},
"broker2":{
"status":"UP",
"details":{
"provider":"ActiveMQ"
}
}
}
},
"livenessState":{
"status":"UP"
},
"readinessState":{
"status":"UP"
}
},
"groups":[
"liveness",
"readiness"
]
}
Under the jms component, there are two brokers - broker1 and broker2. I can configure Actuator to include jms in the readiness group like:
endpoint:
health:
probes:
enabled: true
enabled: true
show-details: always
group:
readiness:
include: readinessState, jms
But, this will include all brokers in the readiness probe.
When calling /actuator/health/readiness, I get:
{
"status":"DOWN",
"components":{
"jms":{
"status":"DOWN",
"components":{
"broker1":{
"status":"DOWN",
"details":{
"error":"javax.jms.JMSException: Failed to create session factory"
}
},
"broker2":{
"status":"UP",
"details":{
"provider":"ActiveMQ"
}
}
}
},
"readinessState":{
"status":"UP"
}
}
}
Since the readiness probe in Kubernetes will only prevent routing web requests to my pod, I have cases where I can process the request if broker1 is down as long as broker2 is up. Is there a way to configure Actuator to include nested health indicators in a Health Group instead of just the root ones? I tried combinations like broker1, jms.broker1, jms/broker1, jms\broker1 to no avail.
If it is not directly supported through configuration, is there a custom component I could create that would give me the desired behavior. For example, I thought of the possibility of writing a custom CompositeHealthContributor, but I am not sure if I can aggregate existing health indicators. I do not want to replicate the health checks that are already being done.
One other related use case is to consider the service as healthy as long as one of a group of external resources is available. For example, I have an identical broker in two data centers. As long as one of the brokers is available, then my service could be considered healthy. What would be a good approach for this use case?
I believe that as long as you expose nested indicators as Health objects, you will get the desired result:
#Component
public class CustomHealthIndicator implements ReactiveHealthIndicator {
private final List<CustomContributor> customContributors;
public CustomHealthIndicator(List<CustomContributor> customContributors) {
Assert.notNull(customContributors, "At least one contributor must be available");
this.customContributors = customContributors;
}
#Override
public Mono<Health> health() {
return checkServices()
.onErrorResume(ex -> Mono.just(new Health.Builder().down(ex).build()));
}
private Mono<Health> checkServices() {
return Mono.fromSupplier(() -> {
final Builder builder = new Builder();
AtomicBoolean partial = new AtomicBoolean();
this.customContributors.parallelStream()
.forEach(customContributor -> {
final Object status = customContributor.getStatus();
final boolean isAnErrorState = status instanceof Throwable;
partial.set(partial.get() || isAnErrorState);
if(isAnErrorState){
builder.withDetail(customContributor.getName(), new Builder().down().withException((Throwable) status).build());
} else {
builder.withDetail(customContributor.getName(), new Builder().up().withDetail("serviceDetail", status).build());
}
});
builder.status(partial.get() ? Status.DOWN : Status.UP);
return
builder.build();
});
}
}
We've been using Flex for about 6 months here at work, and I found that my first batches of FlexUnit tests involving custom components would tend to follow this sort of pattern:
import mx.core.Application;
import mx.events.FlexEvent;
import flexunit.framework.TestCase;
public class CustomComponentTest extends TestCase {
private var component:CustomComponent;
public function testSomeAspect() : void {
component = new CustomComponent();
// set some properties...
component.addEventListener(FlexEvent.CREATION_COMPLETE,
addAsync(verifySomeAspect, 5000));
component.height = 0;
component.width = 0;
Application.application.addChild(component);
}
public function verifySomeAspect(event:FlexEvent) : void {
// Assert some things about component...
}
override public function tearDown() : void {
try {
if (component) {
Application.application.removeChild(component);
component = null;
}
} catch (e:Error) {
// ok to ignore
}
}
Basically, you need to make sure the component has been fully initialized before you can reliably verify anything about it, and in Flex this happens asynchronously after it has been added to the display list. So you need to setup a callback (using FlexUnit's addAsync function) to be notified when that's happened.
Lately i've been just manually calling the methods that the runtime would call for you in the necessary places, so now my tests tend to look more like this:
import flexunit.framework.TestCase;
public class CustomComponentTest extends TestCase {
public function testSomeAspect() : void {
var component:CustomComponent = new CustomComponent();
component.initialize();
// set some properties...
component.validateProperties();
// Assert some things about component...
}
This is much easier to follow, but it kinda feels like I'm cheating a little either way. The first case is slamming it into the current Application (which would be the unit test runner shell app), and the latter isn't a "real" environment.
I was wondering how other people would handle this sort of situation?
I see nothing wrong with using the async version. I can agree that the second version is shorter, but I'm not sure that I think it's easier to follow. The test does a lot of things that you wouldn't normally do, whereas the first example is more true to how you would use the component outside the test environment.
Also, in the second form you have to make sure that you do exactly what the framework would do, miss one step and your test isn't relevant, and each test must repeat this code. Seems to me it's better to test it in a situation that is as close to the real thing as possible.
You could have a look at dpUint's sequences, they made component testing a little more declarative:
public function testLogin():void {
var passThroughData:Object = new Object();
passThroughData.username = "myuser1";
passThroughData.password = "somepsswd";
var sequence:SequenceRunner = new SequenceRunner(this);
sequence.addStep(new SequenceSetter(form.usernameTI, {text:passThroughData.username}));
sequence.addStep(new SequenceWaiter(form.usernameTI, FlexEvent.VALUE_COMMIT, 100));
sequence.addStep(new SequenceSetter(form.passwordTI, {text:passThroughData.password}));
sequence.addStep(new SequenceWaiter(form.passwordTI, FlexEvent.VALUE_COMMIT, 100));
sequence.addStep(new SequenceEventDispatcher(form.loginBtn, new MouseEvent("click", true, false)));
sequence.addStep(new SequenceWaiter(form, "loginRequested", 100));
sequence.addAssertHandler(handleLoginEvent, passThroughData);
sequence.run();
}
(example from the dpUint wiki, see here for more info).