Check attribute of button after click - react-testing-library

Just want to make sure I am doing this idiomatically and the samples I've seen don't appear to show this... When I click a button, I want to check to see an aria attribute has been updated. To get the test to work, I need to re-query for that element. Is that the best way, or should I be awaiting on something?
test("should set aria-selected on tab after click", () => {
let secondTab = getAllByRole("tab")[1];
fireEvent.click(secondTab);
secondTab = getAllByRole("tab")[1]; // Is this proper?
expect(secondTab).toHaveAttribute("aria-selected", "true");

You can wait for your element to be rendered after click, using async wait utilities
test("should set aria-selected on tab after click", () => {
let secondTab = getAllByRole("tab")[1];
fireEvent.click(secondTab);
// wait for appearance
await waitFor(() => {
expect(getAllByRole("tab")[1]).toBeInTheDocument()
})
expect(secondTab).toHaveAttribute("aria-selected", "true");
});

You should be awaiting for DOM changes. And you can use act for that.
import { act } from '#testing-library/react';
test("should set aria-selected on tab after click", async () => {
const secondTab = getAllByRole("tab")[1];
await act(async () => {
fireEvent.click(secondTab);
});
expect(secondTab).toHaveAttribute("aria-selected", "true");
});

Related

How to update events from SignalR in widget at the same time as event happens?

How to poll the event if new data appeared in the event? I am using SinglR package to get data from websocket. It has way to wotk with events from websockets, but my problem is it has the new event inside List, but dosen't automatically shows it in widget until I reload page manually. I am also using streams to get the pile of data to update by itselfs and at the same time as data appeares in event.
I found only one way: it is add Stream.pireodic, but image which comes from event is flickering, but I don't need that behaviour. How can I solve this issue?
The main issue: the widget is not instantly updated and does not show data from the event
P.S Nothing except SignalR library dosen't work with websocket in my case
List wanted = [];
Future<List?> fetchWantedFixation() async {
final httpConnectionOptions = HttpConnectionOptions(
accessTokenFactory: () => SharedPreferenceService().loginWithToken(),
skipNegotiation: true,
transport: HttpTransportType.WebSockets);
final hubConnection = HubConnectionBuilder()
.withUrl(
'http://websockets/sockets',
options: httpConnectionOptions,
)
.build();
await hubConnection.start();
String? wantedFixation;
int? index;
hubConnection.on('Wanted', (arguments) {
Future.delayed(const Duration(seconds: 7))
.then((value) => alarmplayer.StopAlarm());
alarmplayer.Alarm(url: 'assets/wanted.mp3', volume: 0.25);
wanted = arguments as List;
});
hubConnection.onclose(({error}) {
throw Exception(error);
});
print(wanted);
return wanted;
}
// it makes image flickering, but the event are updating
Stream<List> getEvent() async* {
yield* Stream.periodic(Duration(seconds: 5), (_) {
return wanted;
}).asyncMap((event) async => event);
}

Flutter Get Navigator context for async task

When I press the login button, I receive a Future. If true, then I change page, else I display a popup dialog.
onPressed: () async {
final navigator = Navigator.of(context); // store the Navigator to enable .push
bool? res = await sendUser('6#gmail.com', 'password');
// if connection succeeds
if (res) {
navigator.push(MaterialPageRoute(builder: (context) => const newScreen()));
} else {
showPopUp(context);
}
}
I have the warning 'Do not use BuildContexts accros async gaps' for the popup. I had this warning for Navigator.push, so I fixed it by storing the Navigator but I don't know what to do for the popup. Can I get like the context of navigator ?
Try surrounding your showPopUp function inside an if (mounted) like so:
onPressed: () async {
final navigator = Navigator.of(context); // store the Navigator to enable .push
bool? res = await sendUser('6#gmail.com', 'password');
// if connection succeeds
if (res) {
navigator.push(MaterialPageRoute(builder: (context) => const newScreen()));
} else if (mounted) { //Changed here <-------------
showPopUp(context);
}
}
PS: Caution when using ternary operators while this issue is open

Added data is only showing after reloading in flutter

here is a popup screen to add the transaction to the app, as you can see here
and when the add button pressed the data will add to database and also to the dislpay , here is the code
ElevatedButton(
//on pressed
onPressed: () async {
final _categoryName = _nameEditingController.text;
if (_categoryName.isEmpty) {
return;
}
final _type = selectedCategoryNotifier.value;
//sending the data to model class
final _category = CategoryModel(
id: DateTime.fromMillisecondsSinceEpoch.toString(),
name: _categoryName,
type: _type,
);
//inserting the data to database
await CategoryDb.instance.insertCategory(_category);
//refreshing the ui
await CategoryDb.instance.refreshUI();
//and quitting the popup screen
Navigator.of(ctx).pop();
},
child: const Text('Add'),
),
and in this code you can see that I called 2 functions that for insert data and also refresh the UI, in the refresh UI function I added the function that to get all data from database to screen, here the code of all functions for CRUD operatins
const databaseName = 'category-database';
abstract class CategoryDbFunctions {
Future<List<CategoryModel>> getCategories();
Future<void> insertCategory(CategoryModel value);
}
//CRUD operations code
class CategoryDb implements CategoryDbFunctions {
CategoryDb._internal();
static CategoryDb instance = CategoryDb._internal();
factory CategoryDb() {
return instance;
}
ValueNotifier<List<CategoryModel>> incomeCategoryListListener =
ValueNotifier([]);
ValueNotifier<List<CategoryModel>> expenseCategoryListListener =
ValueNotifier([]);
#override
Future<void> insertCategory(CategoryModel value) async {
final _categoryDB = await Hive.openBox<CategoryModel>(databaseName);
await _categoryDB.add(value);
await refreshUI();
}
#override
Future<List<CategoryModel>> getCategories() async {
final _categoryDB = await Hive.openBox<CategoryModel>(databaseName);
return _categoryDB.values.toList();
}
Future<void> refreshUI() async {
final _allCategories = await getCategories();
incomeCategoryListListener.value.clear();
expenseCategoryListListener.value.clear();
await Future.forEach(
_allCategories,
(CategoryModel category) {
if (category.type == CategoryType.income) {
incomeCategoryListListener.value.add(category);
} else {
expenseCategoryListListener.value.add(category);
}
},
);
}
}
so I checked the all things , but I couldn't find where I'm missing parts,
and here is the main part, it is adding to the database also displaying after I refresh the UI or change the tab here you can see what I mean by 'changing the tab'
this is the problem I'm trying to fix this for 2 day, i couldn't find any solution or mistake in my code
There many ways you can handle this problem.
but I dont see where you notify youre ui that the data has been changed, flutter does only update the ui when you use setState etc.. these functions help flutter updating the ui where the data changed.
i would recommend you to use setState in the place you invoke youre dialog.
onTap:(){
setState(){
await dialogStuff();
}
}

Ionic 4 Loading Controller continues after dismissing

I am using Ionic 4 and the Loading Controller.
async presentLoading() {
const loading = await this.loadingCtrl.create({
message: 'wait. . .'
});
return await loading.present();
}
Works great. But now I want to dismiss the loader programatically.
this.loadingCtrl.dismiss().then(a => console.log('dismissed'));
Even though I see 'dismissed' on my console (logging worked) the loading overlay continues. Any idea what might be the reason?
You not dismissing the actual loader that is displayed. You have to dismiss it with loading variable like below :
loading.dismiss().then(a => console.log('dismissed'));
Notice that I have used variable loading that you have declared for current loader.
If you want to dismiss programmatically use this in your service.
export class LoaderService {
private isLoading = false;
constructor(private loadingController: LoadingController) {
}
async presentLoading() {
// issue generated! so we used Boolean value to set loader dismissed call
firstly so we used this logic
this.isLoading = true;
let loading = await this.loadingController.create({
message: 'Please Wait',
spinner: 'bubbles'
}).then((res) => {
res.present();
if (!this.isLoading) {
// res.dismiss().then(()=> console.log('abort presenting'));
this.loadingController.dismiss().then(() =>
console.log('Dismissed'));
}
});
return loading;
}
async hideLoading() {
this.isLoading = false;
return await this.loadingController.dismiss().then(() => console.log('Dismissed'));
}
}

Ionic4 or 3 LoadingController (progress dialog)

I am using ionic4 LoadingController in the app. it is dismissed after a given interval of time, but don't want like that. I don't want to set the time. before calling web api i will display LoadingController and I want to dismiss it whenever I will get the response from rest service. can anyone help me how to implement it?
You should do something like this:
async function presentLoading() {
const loader= document.querySelector('ion-loading-controller');
await loader.componentOnReady();
const element = await loader.create({
message: 'Please wait...',
spinner: 'crescent',
duration: 2000
});
return await element .present();
}
// Initialize variable
private loader: any;
private loaderActive: boolean = false;
// Generic method to display the loader
async showLoader() {
this.loaderActive = true;
this.loader = await this.loadingCtrl.create({
message: 'Please wait...',
spinner: 'crescent',
});
await this.loader.present();
}
// Generic method to dismiss the loader
async dismissLoader() {
if (this.loaderActive === true) {
await this.loader.dismiss();
}
this.loaderActive = false;
}
Before api call just call the method this.showLoader(), once you get the response
just call the this.dismissLoader() method.
I would take a guess and say that you are using HttpClient to get your data from your rest service, therefore I would propose this scenario for you,
First of all we start by displaying the loader:
let loader = await this.loadingCtrl.create({
//Your loader content and options
});
loader.present();//show the loader
After that we get the data using the HttpClient. let suppose that we injected that's way public http: HttpClient so it depends on your rest service http mthod (post, get, put ...). the code would be:
this.http.get("url").subscribe((data: any) => {
//process your data here
loader.dismiss();//then we hide the loader
});
Leave me a comment if I misunderstood it.