I’m not sure, but this may be because String texts are handled in Notifications differently than Components. This is how Notification::setText looks like (in vaadin 14 at least):
The given text is defined as ‘innerHTML’ of the notification element. Also important here is that this.removeAll() is called, which removes any previously added child components. This sounds like they are aware that innerHTML texts do not go well with any child components.
So what can you do if you want both text and button?
Instead of passing a String into constructor, wrap the String in a Text or Span component. This will get added as child component like the Button.
Notification notification = new Notification(new Text("test"));
notification.add(new HorizontalLayout(new Button("testButton"))); // maybe even add the text directly into the horizontalLayout?
notification.open();
Not so easy to find, but there are Javadoc about this:
On Notification(Component... components) constructor
Note: To mix text and child components in a component that also supports child components, use the Text component for the textual parts.
On add(Collection<Component> components) and addComponentAtIndex(int index, Component component) method
NOTE: When mixing this method with Notification(String), Notification(String, int) and Notification(String, int, Notification. Position) method will remove the text content.
So, similar to what @Kaspar4 suggested, wrap the text string into a component, e.g. Text or add a Span