richie-education 3.2.0 → 3.2.1-dev3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -104,7 +104,7 @@ const Total = () => {
104
104
  const { product, offering, enrollment } = useSaleTunnelContext();
105
105
  const totalPrice =
106
106
  enrollment?.offerings?.[0]?.rules?.discounted_price ??
107
- offering?.rules.discounted_price ??
107
+ offering?.rules?.discounted_price ??
108
108
  product.price;
109
109
  return (
110
110
  <div className="sale-tunnel__total">
@@ -496,7 +496,7 @@ describe.each([
496
496
  expect($totalAmount).toHaveTextContent(
497
497
  'Total' +
498
498
  formatPrice(
499
- enrollmentDiscounted.offerings[0].rules.discounted_price!,
499
+ enrollmentDiscounted.offerings[0].rules?.discounted_price!,
500
500
  product.price_currency,
501
501
  ).replace(/(\u202F|\u00a0)/g, ' '),
502
502
  );
@@ -569,7 +569,7 @@ describe.each([
569
569
  const $totalAmount = screen.getByTestId('sale-tunnel__total__amount');
570
570
  expect($totalAmount).toHaveTextContent(
571
571
  'Total' +
572
- formatPrice(offering!.rules.discounted_price!, product.price_currency).replace(
572
+ formatPrice(offering!.rules!.discounted_price!, product.price_currency).replace(
573
573
  /(\u202F|\u00a0)/g,
574
574
  ' ',
575
575
  ),
@@ -200,7 +200,7 @@ export interface OfferingRule {
200
200
 
201
201
  export interface Offering extends OfferingLight {
202
202
  is_withdrawable: boolean;
203
- rules: OfferingRule;
203
+ rules?: OfferingRule;
204
204
  }
205
205
  export function isOffering(
206
206
  entity: CourseListItem | OfferingLight | RichieCourse,
@@ -33,7 +33,7 @@ const CourseProductItemFooter = ({
33
33
  offering,
34
34
  canPurchase,
35
35
  }: CourseProductItemFooterProps) => {
36
- if (!offering.rules.has_seats_left)
36
+ if (!offering?.rules?.has_seats_left)
37
37
  return (
38
38
  <p className="product-widget__footer__message">
39
39
  <FormattedMessage {...messages.noSeatsAvailable} />
@@ -50,7 +50,7 @@ const CourseProductItemFooter = ({
50
50
  disabled={!canPurchase}
51
51
  buttonProps={{ fullWidth: true }}
52
52
  />
53
- {offering.rules.has_seat_limit && (
53
+ {offering?.rules?.has_seat_limit && (
54
54
  <p className="product-widget__footer__message">
55
55
  <FormattedMessage
56
56
  {...messages.nbSeatsAvailable}
@@ -194,7 +194,7 @@ describe('CourseProductItem', () => {
194
194
  const discountedPriceLabel = screen.getByText('Discounted price:');
195
195
  expect(discountedPriceLabel.classList.contains('offscreen')).toBe(true);
196
196
  const discountedPrice = screen.getByText(
197
- priceFormatter(product.price_currency, offering.rules.discounted_price!).replace(
197
+ priceFormatter(product.price_currency, offering.rules!.discounted_price!).replace(
198
198
  /(\u202F|\u00a0)/g,
199
199
  ' ',
200
200
  ),
@@ -257,7 +257,7 @@ describe('CourseProductItem', () => {
257
257
  const discountedPriceLabel = screen.getByText('Discounted price:');
258
258
  expect(discountedPriceLabel.classList.contains('offscreen')).toBe(true);
259
259
  const discountedPrice = screen.getByText(
260
- priceFormatter(product.price_currency, offering.rules.discounted_price!).replace(
260
+ priceFormatter(product.price_currency, offering.rules!.discounted_price!).replace(
261
261
  /(\u202F|\u00a0)/g,
262
262
  ' ',
263
263
  ),
@@ -838,4 +838,39 @@ describe('CourseProductItem', () => {
838
838
  expect(screen.queryByRole('button', { name: product.call_to_action })).not.toBeInTheDocument();
839
839
  screen.getByText('Sorry, no seats available for now');
840
840
  });
841
+
842
+ it('renders product information without rules in offering', async () => {
843
+ const offering = OfferingFactory({
844
+ product: CredentialProductFactory({
845
+ price: 840,
846
+ price_currency: 'EUR',
847
+ }).one(),
848
+ rules: undefined,
849
+ }).one();
850
+ const { product } = offering;
851
+ fetchMock.get(
852
+ `https://joanie.endpoint/api/v1.0/courses/00000/products/${product.id}/`,
853
+ offering,
854
+ );
855
+
856
+ render(
857
+ <CourseProductItem
858
+ course={PacedCourseFactory({ code: '00000' }).one()}
859
+ productId={product.id}
860
+ />,
861
+ { queryOptions: { client: createTestQueryClient({ user: null }) } },
862
+ );
863
+
864
+ // Wait for product information to be fetched
865
+ await screen.findByRole('heading', { level: 3, name: product.title });
866
+
867
+ // Expect to render the component without rules information
868
+ expect(
869
+ screen.getByText(
870
+ priceFormatter(product.price_currency, product.price).replace(/(\u202F|\u00a0)/g, ' '),
871
+ ),
872
+ ).toBeInTheDocument();
873
+ expect(document.querySelector('.product-widget__price-discounted')).not.toBeInTheDocument();
874
+ expect(screen.getByText('Sorry, no seats available for now')).toBeInTheDocument();
875
+ });
841
876
  });
@@ -103,7 +103,7 @@ const Header = ({ product, order, offering, hasPurchased, canPurchase, compact }
103
103
  return null;
104
104
  }
105
105
 
106
- if (offering.rules.discounted_price != null) {
106
+ if (offering?.rules?.discounted_price != null) {
107
107
  return (
108
108
  <>
109
109
  <span id="original-price" className="offscreen">
@@ -133,7 +133,7 @@ const Header = ({ product, order, offering, hasPurchased, canPurchase, compact }
133
133
  return (
134
134
  <FormattedNumber currency={product.price_currency} value={product.price} style="currency" />
135
135
  );
136
- }, [canPurchase, offering.rules.discounted_price, product.price]);
136
+ }, [canPurchase, offering?.rules?.discounted_price, product.price]);
137
137
 
138
138
  return (
139
139
  <header className="product-widget__header">
@@ -144,10 +144,10 @@ const Header = ({ product, order, offering, hasPurchased, canPurchase, compact }
144
144
  {hasPurchased && <FormattedMessage {...messages.purchased} />}
145
145
  {displayPrice}
146
146
  </strong>
147
- {offering?.rules.description && (
147
+ {offering?.rules?.description && (
148
148
  <p className="product-widget__header-description">{offering.rules.description}</p>
149
149
  )}
150
- {offering?.rules.discounted_price && (
150
+ {offering?.rules?.discounted_price && (
151
151
  <p className="product-widget__header-discount">
152
152
  {offering.rules.discount_rate ? (
153
153
  <span className="product-widget__header-discount-rate">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "richie-education",
3
- "version": "3.2.0",
3
+ "version": "3.2.1-dev3",
4
4
  "description": "A CMS to build learning portals for Open Education",
5
5
  "main": "sandbox/manage.py",
6
6
  "scripts": {