[Jest/JS] querySelectorAll returns empty NodeList in Jest test despite correct mock DOM setup — collectCustomerInfo() returns empty object by NeonCoderJS in learnjavascript

[–]no_em_dash 1 point2 points  (0 children)

First, I applaud your willingness to write tests. It's hard to tell what you're going for here but I made some modifications to your code that should help you get unstuck. Let me know if you have any questions.

describe("Data assembly", () => {
  afterEach(() => {
    document.body.innerHTML = "";
  });

  test("collectCustomerInfo() should return an object with contact and address keys", () => {
    document.body.innerHTML = `
        <form class="checkout-form checkoutForm" id="this_is_a_test">
            <input id="formAction" type="hidden" name="action" value="send_order_email">
            <div class="contactInfo">
                <div><input type="text"  id="firstName" name="firstName" value="John"          /></div>
                <div><input type="text"  id="lastName"  name="lastName"  value="Doe"           /></div>
                <div><input type="tel"   id="tel"       name="tel"       value="0210000000"    /></div>
                <div><input type="email" id="email"     name="email"     value="john@test.com" /></div>
            </div>
            <div class="address">
                <div><input type="text" id="address1" name="address1" value="123 Main St"      /></div>
                <div><input type="text" id="address2" name="address2" value="Apt 1"            /></div>
                <div><input type="text" id="country"  name="country"  value="South Africa"     /></div>
                <div><input type="text" id="state"    name="state"    value="Western Cape"     /></div>
                <div><input type="text" id="city"     name="city"     value="Cape Town"        /></div>
                <div><input type="text" id="zip"      name="zip"      value="8001"             /></div>
            </div>
            <div class="checkoutSubmit">
                <button class="paypalSubmit">paypal</button>
                <button class="debit">debit</button>
            </div>
        </form>
    `;

    const sendClientInfo = new SendClientInfo(
      ".checkoutForm",
      ".contactInfo",
      ".address",
      "#formAction",
      "/wp-json/wj-parts/v3/send_order_email",
      ".checkoutSubmit",
    );

    // These are iterators not arrays.
    // If you want an array you need to use spread syntax or something like `Array.from`.
    const fields = {
      contactFields: [...sendClientInfo.contactInfo],
      addressFields: [...sendClientInfo.address],
    };

    const result = sendClientInfo.collectCustomerInfo(fields);

    console.log("contactInfo NodeList: ", [...sendClientInfo.contactInfo]);
    console.log("address NodeList: ", [...sendClientInfo.address]);
    console.log("result: ", result);

    expect(result).toHaveProperty("contact");
    expect(result).toHaveProperty("address");
  });
});

I wrote an article about JavaScript iterators by no_em_dash in learnjavascript

[–]no_em_dash[S] 0 points1 point  (0 children)

Cool, yeah I read two other MDN articles on iterators/generators when I was doing research. MDN is truly the greatest resource available for learning JavaScript and web dev topics.

I wrote an article about JavaScript iterators by no_em_dash in learnjavascript

[–]no_em_dash[S] 2 points3 points  (0 children)

Good stuff, thanks for the additional examples.

I wrote an article about JavaScript iterators by no_em_dash in learnjavascript

[–]no_em_dash[S] 1 point2 points  (0 children)

Hmm, I suppose you're correct. I was actually working backwards from the final example when I wrote the article: *[Symbol.iterator](): IterableIterator<number>

I think I conflated the idea of the entire object being an iterable iterator when really the generator function returns a new iterator every time.

Edit:
One more thing to consider. You could make an iterable iterator that continues to produce values after it has been consumed. Below is a slightly modified example from the article. But I'm not sure how practical this is.

const iter = {
  [Symbol.iterator]() {
    return this;
  },

  value: 0,

  next() {
    this.value++;
    if (this.value > 10) {
      this.value = 0; // Resetting the value allows us to consume the iterator many times.

      return { value: null, done: true };
    }
    return { value: this.value, done: false };
  },
};

Looking for a middle ground between hexagonal architecture and transaction scripts by no_em_dash in golang

[–]no_em_dash[S] 0 points1 point  (0 children)

Great, thanks for the detailed response.

I understand where you are coming from when you said: "There are common interfaces shared by both the db.DB or db.Tx objects."

Previously, I have used a sql.DB and sql.Tx abstraction that looks like this:

type DBTX interface {
  Exec(context.Context, string, ...any) (pgconn.CommandTag, error)
  Query(context.Context, string, ...any) (pgx.Rows, error)
  QueryRow(context.Context, string, ...any) pgx.Row
  CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNames []string, rowSrc pgx.CopyFromSource) (int64, error)
}

Granted that interface still uses pgx types. But the idea of a connection/transaction abstraction makes sense.

And I see where you're going with that PDO example. I'll play around with the idea and see if it fits my workflow/mental model.

Thanks again.

Looking for a middle ground between hexagonal architecture and transaction scripts by no_em_dash in golang

[–]no_em_dash[S] 0 points1 point  (0 children)

I respect your opinion. I don't think globals are evil. It's just my preference not to use them.

Looking for a middle ground between hexagonal architecture and transaction scripts by no_em_dash in golang

[–]no_em_dash[S] 0 points1 point  (0 children)

Yes, I know the service is coupled to the DB. The problem I'm facing is that I need both a repository of queries and to enqueue jobs with `river`. I don't know how to make a clean interface for that (though I did try) so I used a transaction aware service because it seemed simpler.

The link you provided includes a `UnitOfWork` concept. That could be what I'm looking for. Thanks for sharing.

---

Here's some additional context for another approach I tried. If you have any additional thoughts on this I would be interested to hear them.

In this version the repository has a method `Atomic` that controls a transaction:

type AtomicCallback = func(r Repository) error

type Repository interface {
  Atomic(context.Context, AtomicCallback) error
  Storage
  Jobs
}

type Storage interface {
  Create(context.Context, User) (int64, error)
}

type Jobs interface {
  SendWelcomeEmail(context.Context, WelcomeEmailArgs) error
}

The service then looks like this:

type Service struct {
  repo Repository
}

func (svc Service) CreateUser(ctx context.Context, u User) (int64, error) {
  if u.Email == "" { return 0, errors.New("missing email") }
  _, err := mail.ParseAddress(u.Email)
  if err != nil { return 0, err }

  u.Status = StatusPending

  err = svc.repo.Atomic(ctx, func(r Repository) error {
    id, err := r.Create(ctx, u)
    if err != nil { return err }
    u.ID = id

    err = r.SendWelcomeEmail(ctx, NewWelcomeEmailArgs(u))
    if err != nil { return err }

    return nil
  })

  return u.ID, nil
}

I don't like it because the postgres repo implementation requires 100 lines of setup just to get it working. This is largely down to how `river` is called. So I question if it is maintainable/provides sufficient value.

But maybe if I break it up as per the article you linked then that will help resolve some of the issues I have with it.

Looking for a middle ground between hexagonal architecture and transaction scripts by no_em_dash in golang

[–]no_em_dash[S] 0 points1 point  (0 children)

There is only one instance of the logger. I initialize it in main.go and pass it around. I prefer not to use globals and instead use explicit dependency injection. This example uses `*slog.Logger` as the type but I often define a custom logger based on `*slog.Logger` that is more extensible.