Things I learned: some 90s arabic pop

A few days ago I was in one of my favorite locations in my city quarter of Köln, Café Casablanca. While drinking my green tea with Morrocan mint, I noticed a song and especially it’s very catchy hook. I used my phone to give me a title and saved it for later.

Later at home, my wife, son and me listened to it together:

I didn’t know the artist. Here’s what Wikipedia has to say about him:

Khaled is one of the most important musicians in the history of Raï music in his native Algeria and is one the world’s best-known Arab singers.[2] To date, Khaled has sold over 80.5 million albums (10 diamond, platinum, and gold) worldwide, making him one of the bestselling Arabic-language singers in history. Among his most famous songs are “Aïcha“, “Didi“, “El Arbi”, “Abdel Kader“, “La Poupée qui fait non“, “Wahran Wahran”, “Bakhta”, “C’est la vie“, and “Alech Taadi”.

He holds the Guinness World Record for best-selling artist of raï music.

Wikipedia

To lower our ignorance a bit further, we figured we could listen to his most famous song, Aicha:

This at least sounded a little familiar. Turns out we knew a cover by Outlandish:

When else should I check out, before I go back to listening to OBSCURA?

Things I learned: transpiling React Native JavaScript with babel in 2025

At work we’re using React Native to build our Android and iOS app. We use it with Hermes, a JavaScript engine Facebook/Meta built for React Native, which doesn’t have a JIT compiler, but is instead optimized for fast startup, by compiling JavaScript to a type of bytecode during build time.

Hermes supports plenty of modern JavaScript features, yet any current React Native app will have its source code processed by a bunch of babel plugins, transforming modern JS code into not-so-modern one.

In our case this bothered me in particular when debugging events handlers that make use of JavaScript’s async/await feature. Hermes supports this natively, but one of the babel plugins included in the build process, would replace each instance with generators (also a relatively new JS feature, but not quite as new as async/await). The resulting stacktrace, if not properly mapped to the original source code, is much harder to read, since there’s usually two extra items in the stack, one referring to a function injected by babel called _asyncToGenerator.

Trying to stop this transform from happening turned into quite a deep rabbit whole. I’ll summarize here what we learned:

  1. our babel.config.js uses babel-preset-expo as the only present
  2. babel-preset-expo in turn uses @react-native/babel-preset as a preset
  3. @react-native/babel-preset includes @babel/plugin-transform-async-to-generator as a plugin
  4. @babel/plugin-transform-async-to-generator itself transform async functions to generators!

To figure out this chain of dependencies, npm ls was useful:

npm ls @babel/plugin-transform-async-to-generator
└─┬ [email protected]
  └─┬ [email protected]
    └─┬ @react-native/[email protected]
      └── @babel/[email protected]

None of these presets have any configuration options we could find, so in the end we decided to add another patch with patch-package:

diff --git a/node_modules/@react-native/babel-preset/src/configs/main.js b/node_modules/@react-native/babel-preset/src/configs/main.js
index 077e1a6..d27d775 100644
--- a/node_modules/@react-native/babel-preset/src/configs/main.js
+++ b/node_modules/@react-native/babel-preset/src/configs/main.js
@@ -133,7 +133,7 @@ const getPreset = (src, options) => {
     extraPlugins.push([
       require('@babel/plugin-transform-async-generator-functions'),
     ]);
-    extraPlugins.push([require('@babel/plugin-transform-async-to-generator')]);
+    // extraPlugins.push([require('@babel/plugin-transform-async-to-generator')]);
   }
   if (
     isNull ||

This is now the 10th patch we’re maintaining, despite using renovate for greenkeeping.

After we verified our app still works fine without this transform, I figured I can at least ask if this could be upstreamed to @react-native/babel-preset. In short: no, at least not yet. From the community discussion post:

  • @react-native/babel-preset needs to support JSC (ie Safari on iOS >= 15.4, and the version of JSC we build for Android) as well as Hermes, so support needs to be universal before we remove plugins completely, but we can gate individual plugins by target (isHermes).
  • There may be runtime performance implications, we can check with the Hermes team or run an experiment case-by-case on that.
  • Static Hermes is coming soon-ish, which will have a very different level of language support and performance characteristics, so it might make sense to hold off for that.
discussions-and-proposals, Remove @babel/plugin-transform-async-to-generator from @react-native/babel-preset

Maybe in a year or so.

If you liked this post, boost it on Mastodon. Please reply there with comments, questions or ideas.

Decision Record: Mocking in Jest tests

This is a lightly edited copy of an Architecture Decision Record I wrote at work and share here in public.

Date: 2023-09-06

Background

We use a lot of mocking in tests written with Jest whenever modules need to leave the JavaScript system barrier. That is, whenever we make network requests, access the location, microphone or camera or some other device interaction, we have to mock those interactions. The rest of this document will call these “system interactions”.

The approach to mocking system interactions currently varies a lot, since we have no convention or agreement around these.

Reasons for Decision

To make Jest tests easier to write and read and increase consistency, we should decide on guidelines for mocking system interactions.

Options

Option 1: Focus on unit testing

Whenever a module (a React component, some other functions) depends on other modules that have system interactions, we mock those direct dependencies, so that the module under test only depends on pure functions or mocks. We mock as closely as possible to the tested module. That could mean jest.mock('@tanstack/react-query') (when testing a custom hook using react-query) or jest.mock('../hooks/useGetUserData') (when testing a component that uses a custom hook).

Since we mock a lot of our own code, we’ll use Manual Mocks correctly to reuse mocks and avoid duplicating mocks many times. Relates to ‘Bonus option 2’ below.

This doesn’t mean that we would mock all dependencies of a module – that would lead to an excessive amount of mocking with little additional value. The focus is on mocking those dependencies that directly or indirectly involved system interactions.

  • Pros:
    • Tests focus on one module and don’t break when dependencies change
  • Cons:
    • Need to write a test for each module, since there is no indirect coverage
    • We’d write a lot of mocks, even for our own code, which could hide actual bugs if the mocks don’t reflect the actual module correctly

Option 2: Focus on integration testing

Whenever a module (a React component, some other functions) depends on other modules that have system interactions we mock the system APIs, like using nock to mock the network requests or mocking react-native-permissions. If a component uses a custom hook that uses react-query to fetch some data, we’d mock the request for “fetch some data”, not the hook or react-query.

Tests without any system interactions won’t need any mocking, for example a pure function can be covered with a neat unit test without mocks.

  • Pros:
    • Increased test coverage of our own code – a single test can cover more than just the module under test
    • Since we only mock system interactions on the “outside”, we can reuse a lot of mocks and don’t need to write them so often. See ‘Bonus option 2’ below for more details.
    • Mocking network requests is more precise than mocking react-query or custom hooks – tests get closer to what the user does, with better runtime than e2etests
  • Cons:
    • Tests can break (false negatives) when imported modules change

Option 3: Combine 1 + 2, label clearly

We use both approaches of mocking outlined as option 1 and 2 above, but clearly label those tests.

// mocks direct dependencies if system interactions are involved
describe('useRecording unit tests', () => {
// mocks only system interactions, not direct dependencies
describe('AssignRecordingGroupModal integration tests', () => {

Within those categories, we apply the rules above, mocking system interactions as close as possible (unit tests) or as far away as possible (integrations), and don’t mix it up.

  • Pros:
    • More flexibility in how we write tests, leaves this decision up to the author
    • A bit more consistency over the status quo, since we at least introduce definitions of unit and integration tests and how they are allowed to deal with system interactions
  • Cons:
    • Need to decide between unit and integration test for each module with system interactions, and potentially defend that decision
    • All the cons of both options

Bonus option 1: Limit jest API usage for mocking

We agree to always use jest.mock() and to forbid usage of jest.spyOn. This is mostly based on various problems we’ve found related to jest.spyOn when trying to switch from babel to swc.

For creating mocks that need to mock a module, but only customize some of it, we try to use jest.createMockFromModule(moduleName)

Bonus option 2: Convention for local vs global mocks

If a mock for a library (module loaded from node_modules) is needed in more than two test files, we move it to __mocks__ (in the root folder). The file name must match the module name used for importing the module, like __mocks__/lodash.js to mock lodash. If named correctly, these mocks are automatically loaded! There’s more details about this in the Jest docs – note the exception about built-in nodejs modules.

Since those mocks are automatically loaded, we try to (incrementally) replace all mocks from jest.config.js‘s setupFilesAfterEnv property and also replace them from setup.jest.js.

Results

Adopt Option 2 (as above) along with the bonus options.

This is the best option to increase test coverage and consistency while keeping the number of mocks low.

We can revisit this decision if it turns out that this focus on integration tests has more or larger drawbacks than estimated here. We could then switch to Option 1 or 3, or refine our approach with Option 2.

Sources

Street coffee in Bulgaria

In Germany it’s very common for people to buy coffee in bakeries, where the person behind the counter will put the paper cup in a “fully” automated coffee machine and press the right button, then hand it out in exchange for money. In terms of potential automation and self-service, it’s a silly process.

In contrast, in Varna in Bulgaria I’ve seen many street coffee vending machines. Sometimes they feature brands like Lavazza, but mostly they’re unbranded and give you different types or mixes of coffee for very little money. Like a double espresso of reasonable taste, surely no worse than the machines in German bakeries, for 60 stotinki – 30 Euro cents!

Coffee vending machine with it’s owner

This one is maintained by Stefan (Стефан), from whom my father in law bought some kilograms of coffee for the mountain vacation. He operates 70 of these around Varna. I didn’t see any other of his, probably because there’s hundreds of them all around the city.

I didn’t learn where Stefan gets his coffee, but he’s got about 16 tons of it stored in the room behind the machine above.

Two tons of coffee
Parts of the coffee machines

A few days later we were in Vinarsko, a little village south of Aytos, around Burgas. There was no coffee in the house, so the need for coffee had to get satisfied by the vending machine in the village centre, located in front of the only store.

The coffee machine next to the store entrance (closed on Sunday)

This machine has so many stories to tell! Look at all the scratches:

The machine in all it’s glory

I guess an important part of coin-fed vending machines is “reading” the coins. Part of that experience is inserting the correct amount, but the machine then rejects it immediately. Another attempt might be accepted, or not. A countermeasure, whatever the mechanics are, seems to be to rub the side of the coin against the metal of the machine – as can be seen here, that has been done excessively!

The main panel

Note the new metal plate on the right. It says търкай тук, “rub here”. I’m not sure if it’s really new, or has been there and ignored for a long time.

When coins get rejected or change gets returned, you get to pick them up a little below – if you dare touch this little hole from hell:

The coin return, half burned away

Since these machines sell something, they also need to pay taxes. For this machine, that’s the most recent looking part. I didn’t understand if it only keeps track of purchases or actually reports them remotely immediately. Given Bulgaria has very good mobile network coverage, I wouldn’t be surprised if this contains a mobile connection:

I don’t know what the 2nd row means, but it seems tax related

That’s all for now about Bulgarian street coffee.

Next up on this trip is Chepelare, in the Rhodope mountains. I’ll keep an eye out for more of these machines.