text:
/**
 * External dependencies
 */
import { renderToStaticMarkup } from '@googleforcreators/react';
import { render } from '@testing-library/react';
import { PAGE_WIDTH, PAGE_HEIGHT } from '@googleforcreators/units';
import { MaskTypes } from '@googleforcreators/masks';
import { registerElementType } from '@googleforcreators/elements';
import { elementTypes } from '@googleforcreators/element-library';
import {
  queryByAutoAdvanceAfter,
  getByAutoAdvanceAfter,
  queryById,
  getById,
} from '@googleforcreators/test-utils';

/**
 * Internal dependencies
 */
import PageOutput from '../page';

jest.mock('flagged');

const PRODUCT_LAMP = {
  productUrl: 'https://www.google.com',
  productId: 'lamp',
  productTitle: 'Brass Lamp',
  productBrand: 'Lamp Co',
  productPrice: 799.0,
  productPriceCurrency: 'USD',
  productImages: [
    { url: '/examples/visual-tests/amp-story/img/cat1.jpg', alt: 'lamp 1' },
    { url: '/examples/visual-tests/amp-story/img/cat1.jpg', alt: 'lamp 2' },
    { url: '/examples/visual-tests/amp-story/img/cat1.jpg', alt: 'lamp 3' },
    { url: '/examples/visual-tests/amp-story/img/cat1.jpg', alt: 'lamp 4' },
    { url: '/examples/visual-tests/amp-story/img/cat1.jpg', alt: 'lamp 5' },
    { url: '/examples/visual-tests/amp-story/img/cat1.jpg', alt: 'lamp 6' },
  ],
  aggregateRating: {
    ratingValue: 4.4,
    reviewCount: 89,
    reviewUrl: 'https://www.google.com',
  },
  productDetails:
    'One newline after this. \n Two newlines after this. \n\n  Five consecutive newlines after this, should become 2 newlines. \n\n\n\n\n Many consecutive newlines with different spacing and tabs after this, should become 2 newlines. \n          \n\n   \n  \n \n  \n \n I hope it works!',
};

const PRODUCT_ART = {
  productUrl: 'https://www.google.com',
  productId: 'art',
  productTitle: 'Abstract Art',
  productBrand: 'V. Artsy',
  productPrice: 1200.0,
  productPriceCurrency: 'INR',
  productImages: [
    { url: '/examples/visual-tests/amp-story/img/cat1.jpg', alt: 'art' },
  ],
  aggregateRating: {
    ratingValue: 4.4,
    reviewCount: 89,
    reviewUrl: 'https://www.google.com',
  },
};

const PRODUCT_CHAIR = {
  productUrl: 'https://www.google.com',
  productId: 'chair',
  productTitle: 'Yellow chair',
  productPrice: 1000.0,
  productPriceCurrency: 'BRL',
  productText: 'The perfectly imperfect yellow chair',
  productImages: [
    { url: '/examples/visual-tests/amp-story/img/cat1.jpg', alt: 'chair' },
  ],
  aggregateRating: {
    ratingValue: 4.4,
    reviewCount: 89,
    reviewUrl: 'https://www.google.com',
  },
  productDetails:
    'Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere error deserunt dignissimos in laborum ea molestias veritatis sint laudantium iusto expedita atque provident doloremque, ad voluptatem culpa adipisci.',
};

const PRODUCT_FLOWERS = {
  productUrl: 'https://www.google.com',
  productId: 'flowers',
  productTitle: 'Flowers',
  productBrand: 'Very Long Flower Company Name',
  productPrice: 10.0,
  productPriceCurrency: 'USD',
  productIcon: '/examples/visual-tests/amp-story/img/shopping/icon.png',
  productImages: [
    { url: '/examples/visual-tests/amp-story/img/cat1.jpg', alt: 'flowers' },
  ],
  aggregateRating: {
    ratingValue: 4.4,
    reviewCount: 89,
    reviewUrl: 'https://www.google.com',
  },
  productDetails:
    'Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere error deserunt dignissimos in laborum ea molestias veritatis sint laudantium iusto expedita atque provident doloremque, ad voluptatem culpa adipisci.',
};

/* eslint-disable testing-library/no-node-access, testing-library/no-container */

describe('Page output', () => {
  beforeAll(() => {
    elementTypes.forEach(registerElementType);
  });

  describe('aspect-ratio markup', () => {
    let backgroundElement;

    beforeEach(() => {
      backgroundElement = {
        isBackground: true,
        id: 'baz',
        type: 'image',
        mimeType: 'image/png',
        scale: 1,
        origRatio: 9 / 16,
        x: 50,
        y: 100,
        height: 1920,
        width: 1080,
        rotationAngle: 0,
        loop: true,
        resource: {
          type: 'image',
          mimeType: 'image/png',
          id: 123,
          src: 'https://example.com/image.png',
          poster: 'https://example.com/poster.png',
          height: 1920,
          width: 1080,
        },
      };
    });

    it('should render a single layer with no background', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const layers = container.querySelectorAll('amp-story-grid-layer');
      expect(layers).toHaveLength(1);
      const layer = layers[0];
      expect(layer).toHaveAttribute(
        'aspect-ratio',
        `${PAGE_WIDTH}:${PAGE_HEIGHT}`
      );
      expect(layer.firstElementChild).toHaveClass('page-fullbleed-area', {
        exact: true,
      });
      expect(layer.firstElementChild.firstElementChild).toHaveClass(
        'page-safe-area',
        { exact: true }
      );
    });

    it('should render the layer for background', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [backgroundElement],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const layers = container.querySelectorAll('amp-story-grid-layer');
      expect(layers).toHaveLength(2);
      const bgLayer = layers[0];
      expect(bgLayer).toHaveAttribute(
        'aspect-ratio',
        `${PAGE_WIDTH}:${PAGE_HEIGHT}`
      );
      expect(bgLayer.children).toHaveLength(1);
      expect(bgLayer.children[0]).toHaveClass('page-fullbleed-area', {
        exact: true,
      });
      expect(bgLayer.children[0].children[0]).toHaveClass('page-safe-area', {
        exact: true,
      });
    });

    it('should render the layer for background with overlay', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            {
              ...backgroundElement,
              overlay: { color: { r: 0, g: 255, b: 0, a: 0.4 } },
            },
          ],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const overlayLayer = container.querySelector(
        '.page-background-overlay-area'
      );
      expect(overlayLayer).toBeInTheDocument();
    });
  });

  describe('animation markup', () => {
    it('should render animation tags for animations', () => {
      const props = {
        id: '123',
        backgroundColor: { type: 'solid', color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          animations: [
            {
              id: '123',
              targets: ['123', '124'],
              type: 'bounce',
              duration: 1000,
            },
            { id: '124', targets: ['123'], type: 'spin', duration: 1000 },
          ],
          elements: [
            {
              id: '123',
              type: 'video',
              mimeType: 'video/mp4',
              scale: 1,
              origRatio: 9 / 16,
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              loop: true,
              resource: {
                type: 'video',
                mimeType: 'video/mp4',
                id: 123,
                src: 'https://example.com/image.png',
                poster: 'https://example.com/poster.png',
                height: 1920,
                width: 1080,
                length: 99,
              },
            },
            {
              id: '124',
              type: 'shape',
              opacity: 100,
              flip: {
                vertical: false,
                horizontal: false,
              },
              rotationAngle: 0,
              lockAspectRatio: true,
              backgroundColor: {
                color: {
                  r: 51,
                  g: 51,
                  b: 51,
                },
              },
              x: 249,
              y: 67,
              width: 147,
              height: 147,
              scale: 100,
              focalX: 50,
              focalY: 50,
              mask: {
                type: 'circle',
              },
            },
          ],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 11,
      };

      const { container } = render(<PageOutput {...props} />);

      const storyAnimations = container.querySelectorAll('amp-story-animation');
      expect(storyAnimations).toHaveLength(3);
      expect(storyAnimations[0]).toHaveAttribute('trigger', `visibility`);
    });
  });

  describe('page advancement', () => {
    it('should use default value for auto-advance-after', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      await expect(
        queryByAutoAdvanceAfter(container, '7s')
      ).not.toBeInTheDocument();
    });

    it('should use default duration for auto-advance-after', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      await expect(getByAutoAdvanceAfter(container, '7s')).toBeInTheDocument();
    });

    it('should use custom value for auto-advance-after', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          advancement: { autoAdvance: false },
          elements: [],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      await expect(
        queryByAutoAdvanceAfter(container, '7s')
      ).not.toBeInTheDocument();
    });

    it('should use custom duration for auto-advance-after', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          advancement: { autoAdvance: true, pageDuration: 9 },
          elements: [],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      await expect(getByAutoAdvanceAfter(container, '9s')).toBeInTheDocument();
    });

    it('should use default duration for images', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            {
              id: 'baz',
              type: 'image',
              mimeType: 'image/png',
              scale: 1,
              origRatio: 9 / 16,
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              loop: true,
              resource: {
                type: 'image',
                mimeType: 'image/png',
                id: 123,
                src: 'https://example.com/image.png',
                poster: 'https://example.com/poster.png',
                height: 1920,
                width: 1080,
              },
            },
          ],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      await expect(getByAutoAdvanceAfter(container, '7s')).toBeInTheDocument();
    });

    it('should use video element ID for auto-advance-after', async () => {
      const props = {
        id: 'foo',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'bar',
          elements: [
            {
              id: 'baz',
              type: 'video',
              mimeType: 'video/mp4',
              scale: 1,
              origRatio: 9 / 16,
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              loop: false,
              resource: {
                type: 'video',
                mimeType: 'video/mp4',
                id: 123,
                src: 'https://example.com/video.mp4',
                poster: 'https://example.com/poster.png',
                height: 1920,
                width: 1080,
                length: 99,
              },
            },
          ],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const video = queryById(container, 'el-baz-media');
      await expect(video).toBeInTheDocument();
      expect(video).toMatchInlineSnapshot(`
        <amp-video
          artwork="https://example.com/poster.png"
          autoplay="autoplay"
          id="el-baz-media"
          layout="fill"
          poster="https://example.com/poster.png"
        >
          <source
            src="https://example.com/video.mp4"
            type="video/mp4"
          />
        </amp-video>
      `);
      await expect(
        getByAutoAdvanceAfter(container, 'el-baz-media')
      ).toBeInTheDocument();
    });

    it('should use video with volume', async () => {
      const props = {
        id: 'foo',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'bar',
          elements: [
            {
              id: 'baz',
              type: 'video',
              mimeType: 'video/mp4',
              scale: 1,
              origRatio: 9 / 16,
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              loop: false,
              volume: 0.5,
              resource: {
                type: 'video',
                mimeType: 'video/mp4',
                id: 123,
                src: 'https://example.com/video.mp4',
                poster: 'https://example.com/poster.png',
                height: 1920,
                width: 1080,
                length: 99,
              },
            },
          ],
        },
        autoAdvance: true,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const video = queryById(container, 'el-baz-media');
      await expect(video).toBeInTheDocument();
      expect(video).toMatchInlineSnapshot(`
        <amp-video
          artwork="https://example.com/poster.png"
          autoplay="autoplay"
          id="el-baz-media"
          layout="fill"
          poster="https://example.com/poster.png"
          volume="0.5"
        >
          <source
            src="https://example.com/video.mp4"
            type="video/mp4"
          />
        </amp-video>
      `);
      await expect(
        getByAutoAdvanceAfter(container, 'el-baz-media')
      ).toBeInTheDocument();
    });

    it('should use video element ID for auto-advance-after if video is below defaultPageDuration', async () => {
      const props = {
        id: 'foo',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'bar',
          elements: [
            {
              id: 'baz',
              type: 'video',
              mimeType: 'video/mp4',
              scale: 1,
              origRatio: 9 / 16,
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              loop: false,
              resource: {
                type: 'video',
                mimeType: 'video/mp4',
                id: 123,
                src: 'https://example.com/video.mp4',
                poster: 'https://example.com/poster.png',
                height: 1920,
                width: 1080,
                length: 1,
              },
            },
          ],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const video = getById(container, 'el-baz-media');
      await expect(video).toBeInTheDocument();
      expect(video).toMatchInlineSnapshot(`
        <amp-video
          artwork="https://example.com/poster.png"
          autoplay="autoplay"
          id="el-baz-media"
          layout="fill"
          poster="https://example.com/poster.png"
        >
          <source
            src="https://example.com/video.mp4"
            type="video/mp4"
          />
        </amp-video>
      `);
      await expect(
        getByAutoAdvanceAfter(container, 'el-baz-media')
      ).toBeInTheDocument();
    });

    it('should ignore looping video for auto-advance-after and set default instead', async () => {
      const props = {
        id: 'foo',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'bar',
          elements: [
            {
              id: 'baz',
              type: 'video',
              mimeType: 'video/mp4',
              scale: 1,
              origRatio: 9 / 16,
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              loop: true,
              resource: {
                type: 'video',
                mimeType: 'video/mp4',
                id: 123,
                src: 'https://example.com/video.mp4',
                poster: 'https://example.com/poster.png',
                height: 1920,
                width: 1080,
                length: 99,
              },
            },
          ],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      await expect(getByAutoAdvanceAfter(container, '7s')).toBeInTheDocument();
    });
  });

  describe('pageAttachment', () => {
    const BACKGROUND_ELEMENT = {
      isBackground: true,
      id: 'baz',
      type: 'image',
      mimeType: 'image/png',
      origRatio: 1,
      x: 50,
      y: 100,
      scale: 1,
      rotationAngle: 0,
      width: 1,
      height: 1,
      resource: {
        type: 'image',
        mimeType: 'image/png',
        id: 123,
        src: 'https://example.com/image.png',
        poster: 'https://example.com/poster.png',
        height: 1,
        width: 1,
      },
    };

    const TEXT_ELEMENT = {
      id: 'baz',
      type: 'text',
      content: 'Hello, link!',
      x: 50,
      y: PAGE_HEIGHT,
      height: 300,
      width: 100,
      rotationAngle: 10,
      padding: {
        vertical: 0,
        horizontal: 0,
      },
      fontSize: 30,
      font: {
        family: 'Roboto',
        service: 'fonts.google.com',
      },
      color: {
        color: {
          r: 255,
          g: 255,
          b: 255,
          a: 0.5,
        },
      },
    };

    it('should output page attachment if the URL is set', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
          pageAttachment: {
            url: 'https://example.test',
            ctaText: 'Click me!',
            theme: 'dark',
            icon: 'https://example.test/example.jpg',
          },
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const pageOutlink = container.querySelector('amp-story-page-outlink');
      await expect(pageOutlink).toHaveTextContent('Click me!');
      await expect(pageOutlink).toHaveAttribute(
        'cta-image',
        'https://example.test/example.jpg'
      );
      await expect(pageOutlink).toHaveAttribute('theme', 'dark');
      await expect(pageOutlink.firstChild).toHaveAttribute(
        'href',
        'https://example.test'
      );
      await expect(pageOutlink).toBeInTheDocument();
    });

    it('should not output page attachment if the URL is empty', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
          pageAttachment: {
            url: '',
            ctaText: 'Click me!',
          },
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const pageOutlink = container.querySelector('amp-story-page-outlink');
      await expect(pageOutlink).not.toBeInTheDocument();
    });

    it('should not output cta-image if empty', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
          pageAttachment: {
            url: 'https://example.test',
            ctaText: 'Click me!',
            theme: 'dark',
            icon: '',
          },
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const pageOutlink = container.querySelector('amp-story-page-outlink');
      await expect(pageOutlink).toHaveTextContent('Click me!');
      await expect(pageOutlink).not.toHaveAttribute('cta-image');
      await expect(pageOutlink).toBeInTheDocument();
    });

    it('should output rel', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
          pageAttachment: {
            url: 'https://example.test',
            ctaText: 'Click me!',
            theme: 'dark',
            icon: '',
            rel: ['nofollow'],
          },
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const pageOutlink = container.querySelector('amp-story-page-outlink');
      await expect(pageOutlink).toBeInTheDocument();
      await expect(pageOutlink).toHaveTextContent('Click me!');
      await expect(pageOutlink).not.toHaveAttribute('cta-image');

      const pageOutATag = pageOutlink.querySelector('a');
      await expect(pageOutATag).toBeInTheDocument();
      await expect(pageOutATag).toHaveAttribute('rel', 'nofollow');
    });

    it('should not output a link in page attachment area', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...TEXT_ELEMENT,
              link: {
                url: 'http://shouldremove.com',
              },
            },
          ],
          pageAttachment: {
            url: 'http://example.com',
            ctaText: 'Click me!',
          },
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain('Hello, link');
      expect(content).not.toContain('http://shouldremove.com');
    });

    it('should output a link outside of page attachment area', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...TEXT_ELEMENT,
              link: {
                url: 'http://shouldoutput.com',
              },
              y: 0,
              height: 100,
            },
          ],
          pageAttachment: {
            url: 'http://example.com',
            ctaText: 'Click me!',
          },
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain('Hello, link');
      expect(content).toContain('http://shouldoutput.com');
    });

    it('should output a link in page attachment area if page attachment is not set', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...TEXT_ELEMENT,
              link: {
                url: 'http://shouldoutput.com',
              },
            },
          ],
          pageAttachment: null,
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain('Hello, link');
      expect(content).toContain('http://shouldoutput.com');
    });

    it('should print page attachment as the last child element', () => {
      const props = {
        id: '123',
        backgroundColor: { type: 'solid', color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          pageAttachment: {
            url: 'http://example.com',
            ctaText: 'Click me!',
          },
          animations: [],
          elements: [
            {
              id: 'baz',
              type: 'video',
              mimeType: 'video/mp4',
              scale: 1,
              origRatio: 9 / 16,
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              loop: false,
              resource: {
                type: 'video',
                mimeType: 'video/mp4',
                id: 123,
                src: 'https://example.com/video.mp4',
                poster: 'https://example.com/poster.png',
                height: 1920,
                width: 1080,
                length: 99,
              },
              tracks: [
                {
                  track: 'https://example.com/track.vtt',
                  trackId: 123,
                  trackName: 'track.vtt',
                  id: 'rersd-fdfd-fdfd-fdfd',
                  srcLang: '',
                  label: '',
                  kind: 'captions',
                },
              ],
            },
          ],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 11,
      };

      const { container } = render(<PageOutput {...props} />);
      const page = container.querySelector('amp-story-page');
      const pageOutlink = container.querySelector('amp-story-page-outlink');
      expect(pageOutlink).toBeInTheDocument();
      expect(page.lastChild).toBe(pageOutlink);
    });
  });

  describe('background color', () => {
    const BACKGROUND_ELEMENT = {
      id: 'baz',
      type: 'image',
      mimeType: 'image/png',
      origRatio: 1,
      x: 50,
      y: 100,
      scale: 1,
      rotationAngle: 0,
      width: 1,
      height: 1,
      resource: {
        type: 'image',
        mimeType: 'image/png',
        id: 123,
        src: 'https://example.com/image.png',
        poster: 'https://example.com/poster.png',
        height: 1,
        width: 1,
        baseColor: '#00379b',
      },
    };

    it('should output background media base color if available', () => {
      const props = {
        id: '123',
        page: {
          backgroundColor: { color: '#00379b' },
          id: '123',
          elements: [BACKGROUND_ELEMENT],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain('background-color:#00379b');
    });

    it('should output the page background color in case of default background element', () => {
      const props = {
        page: {
          id: '123',
          backgroundColor: { color: { r: 255, g: 255, b: 255, a: 0.5 } },
          elements: [
            {
              id: '123',
              type: 'shape',
              isBackground: true,
              isDefaultBackground: true,
              x: 1,
              y: 1,
              width: 1,
              height: 1,
              rotationAngle: 0,
            },
          ],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain('background-color:rgba(255,255,255,0.5)');
    });
  });

  describe('link', () => {
    const BACKGROUND_ELEMENT = {
      isBackground: true,
      id: 'baz',
      type: 'image',
      mimeType: 'image/png',
      origRatio: 1,
      x: 50,
      y: 100,
      scale: 1,
      rotationAngle: 0,
      width: 1,
      height: 1,
      resource: {
        type: 'image',
        mimeType: 'image/png',
        id: 123,
        src: 'https://example.com/image.png',
        poster: 'https://example.com/poster.png',
        height: 1,
        width: 1,
      },
    };

    const TEXT_ELEMENT = {
      id: 'baz',
      type: 'text',
      content: 'Hello, link!',
      x: 50,
      y: PAGE_HEIGHT,
      height: 300,
      width: 100,
      rotationAngle: 10,
      padding: {
        vertical: 0,
        horizontal: 0,
      },
      fontSize: 30,
      font: {
        family: 'Roboto',
        service: 'fonts.google.com',
      },
      color: {
        color: {
          r: 255,
          g: 255,
          b: 255,
          a: 0.5,
        },
      },
    };

    it('should output element with link if the url is set', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...TEXT_ELEMENT,
              link: {
                url: 'https://hello.example',
                desc: 'Hello, example!',
                icon: 'https://hello.example/icon.png',
              },
            },
          ],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain('Hello, example!');
      expect(content).toContain('https://hello.example');
    });

    it('should not output element link if the url is empty', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...TEXT_ELEMENT,
              link: {
                url: '',
                desc: 'Hello, example!',
                icon: 'https://hello.example/icon.png',
              },
            },
          ],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).not.toContain('Hello, example!');
      expect(content).not.toContain('https://hello.example');
    });
  });

  describe('border', () => {
    const BACKGROUND_ELEMENT = {
      isBackground: true,
      id: 'baz',
      type: 'image',
      mimeType: 'image/png',
      origRatio: 1,
      x: 50,
      y: 100,
      scale: 1,
      rotationAngle: 0,
      width: 1,
      height: 1,
      resource: {
        type: 'image',
        mimeType: 'image/png',
        id: 123,
        src: 'https://example.com/image.png',
        poster: 'https://example.com/poster.png',
        height: 1,
        width: 1,
      },
    };

    const MEDIA_ELEMENT = {
      ...BACKGROUND_ELEMENT,
      isBackground: false,
      id: 'baz',
      type: 'image',
    };

    it('should output element with border if border is set', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...MEDIA_ELEMENT,
              border: {
                top: 10,
                left: 10,
                right: 10,
                bottom: 10,
                color: { type: 'solid', color: { r: 255, g: 255, b: 255 } },
              },
            },
          ],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain('border-width:10px 10px 10px 10px;');
      expect(content).toContain('border-color:rgba(255,255,255,1);');
    });

    it('should not output border if the element is not rectangular', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...MEDIA_ELEMENT,
              border: {
                top: 10,
                left: 10,
                right: 10,
                bottom: 10,
                color: { type: 'solid', color: { r: 255, g: 255, b: 255 } },
                position: 'center',
              },
              mask: {
                type: MaskTypes.CIRCLE,
              },
            },
          ],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).not.toContain('border-width:10px 10px 10px 10px;');
      expect(content).not.toContain('border-color:rgba(255,255,255,1);');
    });
  });

  describe('overlay', () => {
    const BACKGROUND_ELEMENT = {
      isBackground: true,
      id: 'baz',
      type: 'image',
      mimeType: 'image/png',
      origRatio: 1,
      x: 50,
      y: 100,
      scale: 1,
      rotationAngle: 0,
      width: 1,
      height: 1,
      resource: {
        type: 'image',
        mimeType: 'image/png',
        id: 123,
        src: 'https://example.com/image.png',
        poster: 'https://example.com/poster.png',
        height: 1,
        width: 1,
      },
    };

    const MEDIA_ELEMENT = {
      ...BACKGROUND_ELEMENT,
      isBackground: false,
      id: 'baz',
      type: 'image',
    };

    it('should output image with linear overlay if set', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...MEDIA_ELEMENT,
              overlay: {
                type: 'linear',
                rotation: 0,
                stops: [
                  { color: { r: 0, g: 0, b: 0, a: 0 }, position: 0 },
                  { color: { r: 0, g: 0, b: 0, a: 1 }, position: 1 },
                ],
                alpha: 0.7,
              },
            },
          ],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain(
        'background-image:linear-gradient(0.5turn, rgba(0,0,0,0) 0%, rgba(0,0,0,0.7) 100%)'
      );
    });

    it('should output video with solid overlay if set', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...MEDIA_ELEMENT,
              type: 'video',
              overlay: {
                color: { r: 0, g: 0, b: 0, a: 0.5 },
              },
            },
          ],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain('background-color:rgba(0,0,0,0.5)');
    });
  });

  describe('borderRadius', () => {
    const BACKGROUND_ELEMENT = {
      isBackground: true,
      id: 'baz',
      type: 'image',
      mimeType: 'image/png',
      origRatio: 1,
      x: 50,
      y: 100,
      scale: 1,
      rotationAngle: 0,
      width: 10,
      height: 10,
      resource: {
        type: 'image',
        mimeType: 'image/png',
        id: 123,
        src: 'https://example.com/image.png',
        poster: 'https://example.com/poster.png',
        height: 1,
        width: 1,
      },
    };

    const MEDIA_ELEMENT = {
      ...BACKGROUND_ELEMENT,
      isBackground: false,
      id: 'baz',
      type: 'image',
      borderRadius: {
        topLeft: 10,
        topRight: 20,
        bottomRight: 10,
        bottomLeft: 10,
      },
    };

    it('should output element with border radius if the radius is set', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [BACKGROUND_ELEMENT, MEDIA_ELEMENT],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain(
        'border-radius:100% 200% 100% 100% / 100% 200% 100% 100%'
      );
    });

    it('should not output border radius if the element is not rectangular', () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            BACKGROUND_ELEMENT,
            {
              ...MEDIA_ELEMENT,
              mask: {
                type: MaskTypes.CIRCLE,
              },
            },
          ],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).not.toContain(
        'border-radius:100% 200% 100% 100% / 100% 200% 100% 100%'
      );
    });
  });

  describe('background audio', () => {
    it('should add background audio', () => {
      const props = {
        id: '123',
        page: {
          backgroundAudio: {
            resource: {
              src: 'https://example.com/audio.mp3',
              id: 123,
              mimeType: 'audio/mpeg',
              length: 100,
              lengthFormatted: '1:40',
            },
            tracks: [],
          },
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const content = renderToStaticMarkup(<PageOutput {...props} />);
      expect(content).toContain(
        'background-audio="https://example.com/audio.mp3"'
      );
      expect(content).not.toContain('amp-video');
    });
    it('should add background audio as amp-video', async () => {
      const props = {
        id: '123',
        page: {
          backgroundAudio: {
            resource: {
              src: 'https://example.com/audio.mp3',
              id: 123,
              mimeType: 'audio/mpeg',
              length: 100,
              lengthFormatted: '1:40',
            },
            tracks: [
              {
                track: 'https://example.com/track.vtt',
                trackId: 123,
                trackName: 'track.vtt',
                id: 'rersd-fdfd-fdfd-fdfd',
                srcLang: '',
                label: '',
                kind: 'captions',
              },
            ],
            loop: true,
          },
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);

      const captions = container.querySelector('amp-story-captions');
      await expect(captions).toBeInTheDocument();
      expect(captions).toMatchSnapshot();
      expect(captions).toHaveAttribute('id', 'el-123-captions');

      const video = container.querySelector('amp-video');
      await expect(video).toBeInTheDocument();
      expect(video).toMatchSnapshot();
      expect(video).toHaveAttribute('captions-id', 'el-123-captions');

      const page = container.querySelector('amp-story-page');
      await expect(page).toBeInTheDocument();
      expect(page).not.toContain(
        'background-audio="https://example.com/audio.mp3"'
      );
    });

    it('should not contain background audio if null', async () => {
      const props = {
        id: '123',
        page: {
          backgroundAudio: null,
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const page = container.querySelector('amp-story-page');
      await expect(page).toBeInTheDocument();
      expect(page).not.toContain('background-audio=');
    });

    it('should use amp-video for non-looping background audio', async () => {
      const props = {
        id: '123',
        page: {
          backgroundAudio: {
            resource: {
              src: 'https://example.com/audio.mp3',
              id: 123,
              mimeType: 'audio/mpeg',
              length: 100,
              lengthFormatted: '1:40',
            },
            tracks: [],
            loop: false,
          },
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const video = container.querySelector('amp-video');
      await expect(video).toBeInTheDocument();
      expect(video).toMatchSnapshot();

      const page = container.querySelector('amp-story-page');
      await expect(page).toBeInTheDocument();
      expect(page).not.toContain(
        'background-audio="https://example.com/audio.mp3"'
      );
    });

    it('should use amp-video with crossorigin="anonymous" for background audio with tracks', async () => {
      const props = {
        id: '123',
        page: {
          backgroundAudio: {
            resource: {
              src: 'https://example.com/audio.mp3',
              id: 123,
              mimeType: 'audio/mpeg',
              length: 100,
              lengthFormatted: '1:40',
            },
            tracks: [
              {
                track: 'https://example.com/track.vtt',
                trackId: 123,
                trackName: 'track.vtt',
                id: 'rersd-fdfd-fdfd-fdfd',
                srcLang: '',
                label: '',
                kind: 'captions',
              },
            ],
            loop: false,
          },
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: false,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const video = container.querySelector('amp-video');
      await expect(video).toBeInTheDocument();
      expect(video).toMatchSnapshot();

      const page = container.querySelector('amp-story-page');
      await expect(page).toBeInTheDocument();
      expect(page).not.toContain(
        'background-audio="https://example.com/audio.mp3"'
      );
    });
  });

  describe('video captions', () => {
    it('should render layer for amp-story-captions', async () => {
      const props = {
        id: 'foo',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'bar',
          elements: [
            {
              id: 'baz',
              type: 'video',
              mimeType: 'video/mp4',
              scale: 1,
              origRatio: 9 / 16,
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              loop: false,
              resource: {
                type: 'video',
                mimeType: 'video/mp4',
                id: 123,
                src: 'https://example.com/video.mp4',
                poster: 'https://example.com/poster.png',
                height: 1920,
                width: 1080,
                length: 99,
              },
              tracks: [
                {
                  track: 'https://example.com/track.vtt',
                  trackId: 123,
                  trackName: 'track.vtt',
                  id: 'rersd-fdfd-fdfd-fdfd',
                  srcLang: '',
                  label: '',
                  kind: 'captions',
                },
              ],
            },
          ],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 7,
      };

      const { container } = render(<PageOutput {...props} />);
      const captions = container.querySelector('amp-story-captions');
      await expect(captions).toBeInTheDocument();
      expect(captions).toMatchInlineSnapshot(`
      <amp-story-captions
        height="100"
        id="el-baz-captions"
        layout="fixed-height"
      />
      `);
    });
  });

  describe('Shopping', () => {
    it('should render shopping attachment if there are products', async () => {
      const props = {
        id: 'foo',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'bar',
          elements: [
            {
              id: 'el1',
              type: 'product',
              x: 50,
              y: 50,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_LAMP,
            },
            {
              id: 'el2',
              type: 'product',
              x: 100,
              y: 100,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_ART,
            },
          ],
        },
      };

      const { container } = render(<PageOutput {...props} />);
      const shoppingAttachment = container.querySelector(
        'amp-story-shopping-attachment'
      );
      await expect(shoppingAttachment).toBeInTheDocument();
    });

    it('should render shopping attachment with custom cta text if there are products', async () => {
      const props = {
        id: 'foo',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'bar',
          shoppingAttachment: {
            theme: 'light',
            ctaText: 'Buy now',
          },
          elements: [
            {
              id: 'el1',
              type: 'product',
              x: 50,
              y: 50,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_LAMP,
            },
            {
              id: 'el2',
              type: 'product',
              x: 100,
              y: 100,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_ART,
            },
          ],
        },
      };

      const { container } = render(<PageOutput {...props} />);
      const shoppingAttachment = container.querySelector(
        'amp-story-shopping-attachment'
      );

      await expect(shoppingAttachment).toBeInTheDocument();
      await expect(shoppingAttachment).toHaveAttribute('cta-text', 'Buy now');
      await expect(shoppingAttachment).toHaveAttribute('theme', 'light');
    });

    it('should not render page attachment if there are products', async () => {
      const props = {
        id: 'foo',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'bar',
          elements: [
            {
              id: 'el1',
              type: 'product',
              x: 50,
              y: 50,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_LAMP,
            },
            {
              id: 'el2',
              type: 'product',
              x: 100,
              y: 100,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_ART,
            },
          ],
          pageAttachment: {
            url: 'http://example.com',
            ctaText: 'Click me!',
          },
        },
      };

      const { container } = render(<PageOutput {...props} />);
      const shoppingAttachment = container.querySelector(
        'amp-story-shopping-attachment'
      );
      await expect(shoppingAttachment).toBeInTheDocument();
      const pageOutlink = container.querySelector('amp-story-page-outlink');
      await expect(pageOutlink).not.toBeInTheDocument();
    });
  });

  describe('AMP validation', () => {
    jest.retryTimes(3, { logErrorsBeforeRetry: true });

    it('should produce valid AMP output', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 11,
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    it('should produce valid AMP output with manual page advancement', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: false,
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    it('should produce valid AMP output with custom page duration', async () => {
      const props = {
        id: 's1',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'p1',
          advancement: { autoAdvance: true, pageDuration: 10 },
          elements: [],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 7,
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    it('should produce valid AMP output with custom manual page advancement', async () => {
      const props = {
        id: 's1',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'p1',
          advancement: { autoAdvance: false },
          elements: [],
        },
        defaultAutoAdvance: true,
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    it('should produce valid AMP output with Page Attachment', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [],
        },
        defaultAutoAdvance: true,
        pageAttachment: {
          url: 'http://example.com',
          ctaText: 'Click me!',
        },
      };
      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    it('should produce valid output with media elements', async () => {
      const props = {
        id: '123',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          elements: [
            {
              id: '123',
              type: 'video',
              mimeType: 'video/mp4',
              scale: 1,
              origRatio: 9 / 16,
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              loop: true,
              resource: {
                type: 'video',
                mimeType: 'video/mp4',
                id: 123,
                src: 'https://example.com/image.png',
                poster: 'https://example.com/poster.png',
                height: 1920,
                width: 1080,
                length: 99,
              },
            },
          ],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 11,
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    it('should produce valid output with animations', async () => {
      const props = {
        id: '123',
        backgroundColor: { type: 'solid', color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          animations: [
            { id: '123', targets: ['123'], type: 'bounce', duration: 1000 },
          ],
          elements: [
            {
              type: 'text',
              id: '123',
              x: 50,
              y: 100,
              height: 1920,
              width: 1080,
              rotationAngle: 0,
              content: 'Hello World',
              color: { type: 'solid', color: { r: 255, g: 255, b: 255 } },
              padding: {
                horizontal: 0,
                vertical: 0,
              },
              font: {
                family: 'Roboto',
                service: 'fonts.google.com',
              },
            },
          ],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 11,
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    it('should produce valid output with background audio', async () => {
      const props = {
        id: '123',
        backgroundColor: { type: 'solid', color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          backgroundAudio: {
            resource: {
              src: 'https://example.com/audio.mp3',
              id: 123,
              mimeType: 'audio/mpeg',
            },
            tracks: [],
          },
          animations: [],
          elements: [],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 11,
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    it('should produce valid output with background audio with captions', async () => {
      const props = {
        id: '123',
        backgroundColor: { type: 'solid', color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          backgroundAudio: {
            resource: {
              src: 'https://example.com/audio.mp3',
              id: 123,
              mimeType: 'audio/mpeg',
            },
            tracks: [
              {
                track: 'https://example.com/track.vtt',
                trackId: 123,
                trackName: 'track.vtt',
                id: 'rersd-fdfd-fdfd-fdfd',
                srcLang: '',
                label: '',
                kind: 'captions',
              },
            ],
          },
          animations: [],
          elements: [],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 11,
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    it('should produce valid output with non-looping background audio', async () => {
      const props = {
        id: '123',
        backgroundColor: { type: 'solid', color: { r: 255, g: 255, b: 255 } },
        page: {
          id: '123',
          backgroundAudio: {
            resource: {
              src: 'https://example.com/audio.mp3',
              id: 123,
              mimeType: 'audio/mpeg',
              length: 100,
              lengthFormatted: '1:40',
            },
            tracks: [],
            loop: false,
          },
          animations: [],
          elements: [],
        },
        defaultAutoAdvance: true,
        defaultPageDuration: 11,
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });

    // eslint-disable-next-line jest/no-disabled-tests -- TODO: Enable once stable.
    it.skip('should produce valid output with shopping products', async () => {
      const props = {
        id: 'foo',
        backgroundColor: { color: { r: 255, g: 255, b: 255 } },
        page: {
          id: 'bar',
          elements: [
            {
              id: 'el1',
              type: 'product',
              x: 50,
              y: 50,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_LAMP,
            },
            {
              id: 'el2',
              type: 'product',
              x: 100,
              y: 100,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_ART,
            },
            {
              id: 'el3',
              type: 'product',
              x: 150,
              y: 150,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_CHAIR,
            },
            {
              id: 'el3',
              type: 'product',
              x: 200,
              y: 200,
              width: 32,
              height: 32,
              rotationAngle: 0,
              product: PRODUCT_FLOWERS,
            },
          ],
        },
      };

      await expect(<PageOutput {...props} />).toBeValidAMPStoryPage();
    });
  });
});

/* eslint-enable testing-library/no-node-access, testing-library/no-container */

redpajama_set_name: RedPajamaGithub


Hard-working, love studying! Management, multilingual expert. Solid academic and business background. Ex-MBA lecturer on strategic marketing, ex-marketing director at Microsoft, journalist, blogger and author of more than 40 books on economics, human resources and communication and 70 articles on mass psychology, economics, society and communication. My articles are reproduced by 50 sites in Greece. I have interviewed well known people from Greece and abroad.
Manage your finances - create personal wealth in crisis!


TCF National Bank (TCF Bank), a subsidiary of TCF Financial Corporation (TCF) (NYSE:TCF), announces today that it raised nearly $170,000 for St. Jude Children's Research Hospital®, which is leading the way the world understands, treats and defeats childhood cancer and other life-threatening diseases.
Customers and team members from 136 branches in Illinois and Wisconsin donated money to help ensure families never receive a bill from St. Jude for treatment, travel, housing or food – because all a family should worry about is caring for their child.
"It was incredible to witness the energy and generosity of our customers and team members from our branches in Illinois and Wisconsin during this inaugural campaign for St. Jude's Research Hospital," said Lou Campos, director of retail banking at TCF Bank. "We are honored to join the fight to end childhood cancer because all kids deserve a chance to live long, healthy lives."
TCF is dedicated to investing in communities where it does business. In addition to supporting St. Jude's Research Hospital in Illinois and Wisconsin, TCF customers and team members since 2011 have donated $2 million to Susan G. Komen and $1.3 million to March of Dimes. TCF also supports financial education by offering the TCF Financial Scholars Program in more than 2,000 high schools to help teens make smarter financial decisions, including supporting Chicago Public Schools and Milwaukee Public Schools.


text:
← A Helping Hand
Phenomenal Motherhood – H/T GrouchyFogie! →
CAIR going after our children
Posted on December 26, 2011 by DCG | 2 Comments
Taxpayer funded children's museum to hold Muslim program
Examiner: The New York City, taxpayer-funded Children's Museum of New York is planning a major exhibition titled, "Muslim Worlds" which is scheduled to begin a long-term engagement in 2014.
According to information provided by the controversial Council on American-Islamic Relations (CAIR), "Muslim Worlds" will have hands-on exhibits, performances and other events geared toward getting children involved with the thousands of years of cultural heritage that has come from the Muslim world. Besides hosting individual visitors, the Children's Museum will extend invitations for school groups including those from public schools.
Although the museum promises to "bring to life the similarities and differences of Muslim cultures," John Jay College Sociology Professor Mucahit Bilici said that exhibits like this operate on the assumption that Muslim people are separate from American society.
Visitors to the Children's Museum of Manhattan dance as part of a festival celebrating Muslim culture."I would say that the major problem with this project is that it treats Muslims as exclusively external people — as foreigners," he said. "I'm sure that a significant percentage of their audience, like the kids who will visit that exhibition, will be Muslim. I wish that they would be aware of this and that they would frame it in a much more inclusive way."
Cyrus McGoldrick, the civil rights manager of the New York Council on American Islamic Relations, argues that the exhibit will do good things for the Muslim community. He said a lot of the civil rights violations that he investigates happen to Muslim kids who are mocked by their peers at school.
But many detractors believe this is an example of liberal-left hypocrisy. Imagine a group of Methodists or Anabaptists holding an exhibition in a government-funded facility? Does anyone believe the politicians and the ACLU would allow such a show to be held in a city-financed facility with audiences being brought in by public school teachers? How long would that last?" asks political strategist and consultant Mike Baker.
"Tolerance is one thing," says former NYPD Officer Edie Aquina, "but why are we bending over backwards for the sake of political correctness. This group CAIR, for example, has repeatedly attacked the NYPD for trying to protect New Yorkers and Americans from radical Islamic terrorists. Will this Muslim World exhibition feature the role of Islam in the slaughter of 3,000 people on 9-11? Or the hate speech directed at Jews?"
Question: Will this museum show beheadings and stonings of women?
h/t Orbusmax
This entry was posted in Children, Culture War, Islam and tagged CAIR. Bookmark the permalink.
Patriot Angel
I praise the Lord for a daughter who is on top of things in her kid's schools. She would never let my grandchildren go to anything involving a) the advocacy of Muslims, b) of homosexuals) c) of planned parenthood/abortion and sex. She lives in the mid-west and things haven't progressed to the point that it has on both coasts and in other major cities. A major prank would be to let chickens or goats loose in the school!
Thanks again everyone…update here…https://fellowshipofminds.wordpress.com/2011/12/26/islamic-true-believers-blow-up-nigerian-christians-on-christmas-day/#comments
For my Liberty
NEWSFLASH John Jay….they MOSTLY ARE FOREIGNERS! and IF the museum would tell the BRUTAL TRUTH about ISLAM (Which would scare the crap out of the kids) then I would say go for it, but you KNOW they are going to try to feed the kids a load of crock about how peaceful Islam is, when in reality they are the MOST evil "religion" in the modern world. They want "religious tolerance" but go to the Middle east and try to build a Christian Church…..ASK the Christians that have had their churches burned just how tolerant they are!
DCG on Mayor Lightfoot silent as shootings/killings keep piling up in Chiraq

redpajama_set_name: RedPajamaCommonCrawl


A lot of firms have started to move from legacy maintenance schedules and tasks based on paper to automated maintenance management systems. This is due to the growth of Computerized Maintenance Management Systems or shortly called as CMMS. They have turned the preventive maintenance industry upside down since their inception in the latter part of the 20th century.
Some of the large corporations depend on CMMS software solutions to a large extend and rightly so, for they have derived loads of benefits by using these applications and solutions.
The future of CMMS solutions is still taking shape and we'll discuss the same in detail in this article. Read ahead for further clarity.
Surveys have shown time and time again that in the recent years, Computerized Maintenance Management Systems have changed the scenario of maintenance management to a large extent.
2. Ability to carry out intense analysis of processes within the organisation, that concern the business.
5. The ability to carry out work orders with a lot more ease.
Ability to speed up work orders and meeting deadlines on time.
The tasks that have been listed above are some of the benefits of using CMMS; there are so many other processes that are carried out within an organization that can be optimized by using CMMS solutions.
Though Computerized Maintenance Management Systems have changed the way in which organizations carry out maintenance management, there are few drawbacks that need to be fixed so that maximum efficiency can be achieved.
Not all organizations have been able to fully utilize the features of the CMMS solutions. Why is this so? This is mainly since these companies haven't updated themselves over time. They've failed to keep them in par with the ever-increasing pace of technology development.
A lot of companies have just implemented only the ERP system and EAM systems alone or as sub module MRO. This is the reason behind why these companies are not able to obtain maximum efficiency out of these maintenance management systems.
The success of CMMS solutions will depend on the listed parameters and how these parameters are tackled by the organisations that use CMMS for their maintenance management processes and procedures to a large extent.
1. The ability of an organization implementing CMMS to be able to adapt to the entire process and then maintain the system as such and foster its functionality for an extended duration.
2. The compatibility factor with existing systems and solutions in the organisation that have been in use before the implementation of CMMS solutions.
4. Software operating platforms in use.
Apart from the parameters mentioned above, there are numerous other parameters as well, that will influence the rate of success of CMMS solutions; however, the ones listed could be considered as the core issues that will determine whether or not an organisation can fully benefit from the implementation of CMMS software.
Let us know if you have any doubts with regards to CMMS solutions. We will be glad to help you out.
Develop Custom Web Applications – How Big Is The Role Of Web Apps?
What Jobs Will Still be Around in 20 Years?
How and Where to use Apple Pay?


text:
using System.Windows.Shapes;

namespace Noside.Wyvern.Weather.Interfaces
{
	public interface IWeatherIcon
	{
		Path ChanceFlurries { get; }
		Path ChanceRain { get; }
		Path ChanceSleet { get; }
		Path ChanceSnow { get; }
		Path ChanceThunderStorms { get; }
		Path Clear { get; }
		Path Cloudy { get; }
		Path Flurries { get; }
		Path Fog { get; }
		Path Hazy { get; }
		Path MostlyCouldy { get; }
		Path MostlySunny { get; }
		Path PartlyCloudy { get; }
		Path PartlySunny { get; }
		Path Rain { get; }
		Path Sleet { get; }
		Path Snow { get; }
		Path Sunny { get; }
		Path ThunderStroms { get; }
		Path Unknown { get; }

		Path GetPath(string icon);
	}
}


House of Usanga, a fashion brand with a distinctive collection for special occasion and everyday wear, has been successful servicing local customers. Wanting to reach out to people outside the shores of Nigeria, they called on us.
We built a responsive ecommerce store from the group up with an integrated payment solution that receives payment from anywhere in the world where Visa and Mastercard is accepted. We employed aggressive caching strategies to ensure we had incredible responsive times for her worldwide audience.
The result is that we exceeded all expectations and remain a crucial asset to House of Usanga and her team.


text:
Met chief earns his spurs by joining mounted branch for football match
Matthew ChampionWednesday 1 Feb 2012 2:51 pm
Metropolitan police Commissioner Bernard Hogan-Howe was last night spotted moonlighting for Scotland Yard's mounted branch at a Premier League football match in London.
Metropolitan police Commissioner Bernard Hogan-Howe frequently joined mounted police operations while being in charge at Merseyside police (Picture: PA)
The country's most senior police officer was on hand, or on horse, to police Tottenham Hotspur versus Wigan Athletic at White Hart Lane in north London.
Mr Hogan-Howe, who got the Met's top job last September following the resignation of his predecessor Sir Paul Stephenson during the phone-hacking affair, has a track record of joining mounted police operations dating back to his time at Merseyside police.
He is no stranger to action either after making headlines for sprinting out of his chauffeur-driven car to apprehend a suspected drink-driver in 2006.
But while the horse-riding and double-barrelled surname may suggest Mr Hogan-Howe comes from an affluent background, he actually added his mother's maiden name by deed poll after she raised him single-handedly.
Being stuck outside White Hart Lane last night meant Mr Hogan-Howe missed a good game as Spurs ran out 3-1 winners over Wigan.
Bernard Hogan-HoweScotland Yard


text:
Im Soo-jung and Jang Dong-gun's "The Crying Man" falls through
2013/08/27 | Permalink | Source
The movie "The Crying Man" fell through.
Im Soo-jung had made up her mind to star in the movie about a killer and a woman who becomes his target. It was one of the most highly expected movies for this year as it was directed by Lee Jeong-beom from "The Man From Nowhere" and starred by Jang Dong-gun.
Im Soo-jung's casting in the movie was also a newly attempted concept.
However, Im Soo-jung decided not to go along with "The Crying Man" for something else.
"The Crying Man" was supposed to start making in September but it looks like that's going to be delayed.
Source : star.moneytoday.co.kr...
Copy & paste guideline for this article
Always put a link back to the source and HanCinema permalink
"Im Soo-jung and Jang Dong-gun's "The Crying Man" falls through"
by HanCinema is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
Based on a work from this source
Related movies/dramas
No Tears for the Dead
(우는 남자 - 2013)
The Man From Nowhere
(아저씨 - 2010)
You're reading the news with potential spoilers, make them spoiler free, dismiss
Nothing is sure for "She Gets Married Thrice"
2013/08/27 (Source)
Kim Su-hyun's new drama "She Gets Married Thrice" claimed that no one has been confirmed as a cast,...More
Kim Seo-hyung's tension with Ha Ji-won
Actress Kim Seo-hyung is attempting at her first historical drama. Kim Seo-hyung has taken on the,...More
Kim Jae-won on the job
Kim Jae-won was seen behind the scenes. Enterprise released pictures of the actor at work in the ,...More


text:
Kroumirie (även Khroumirie) är en bergskedja i nordöstra Algeriet och nordvästra Tunisien. Den är bevuxen av täta skogar, där korkeken dominerar.

Klimatet i området är tempererat, och årsmedeltemperaturen är  °C. Den varmaste månaden är augusti, då medeltemperaturen är  °C, och den kallaste är januari, med  °C. Genomsnittlig årsnederbörd är  millimeter. Den regnigaste månaden är februari, med i genomsnitt  mm nederbörd, och den torraste är juni, med  mm nederbörd.

Källor

Berg i Algeriet
Berg i Algeriet 500 meter över havet eller högre
Berg i Tunisien

redpajama_set_name: RedPajamaWikipedia


Kristin Dobson is the National Field Director for Campus Leadership Programs. Prior to this, she was the Deputy National Field Director, worked as a Regional Field Coordinator, as well as a Leadership Institute Field Representative in the great state of Michigan.
A 2011 graduate of Depaul University, Kristin participated in The Fund for American Studies Capital Semester Program at Georgetown University. She has also interned with the American Legislative Exchange Council, the Heartland Institute, and the Koch Internship Program.


text:
Allyn Young on Increasing Returns | springerprofessional.de Skip to main content
vorheriges Kapitel Alfred Marshall on Organic Growth
nächstes Kapitel Nicholas Kaldor on Equilibrium Economics and Ec...
4. Allyn Young on Increasing Returns
Allyn Young, a Harvard economist, shot into international fame for his contribution to Paris peace conference after WWI.
The use of capital-intensive methods (or roundabout methods) in Young does not depend on the rate of interest but on the size of the market. "[T]he actual industrialists (and bankers) deny that variations in the rate of interest influence the extent of their operations, providing their competitors pay the same. It is the 'market' on which they count" (Young 1990 , p. 82). This point was also noted by Kaldor ( 1972 , p. 1242) who wrote: "[Young] argued that the extent to which capital is used in relation to labour is predominantly a matter of the scale of operations – the capital/labour ratio in production is a function of the extent of the market rather than of relative factor prices".
Young's modesty has been noted by several students and colleagues in letters and obituaries written after his death. In a letter of condolence (dated 17 March 1929) to Mrs. Young after Young's premature death, John M. Keynes wrote: "His was an outstanding personality in the economics world and the most lovable. His influence as a teacher and a critic and as one who would always share with others all his best ideas was far greater than anyone would suppose who only knew his printed words; for it was his own work – unfortunately perhaps – which always came last". Schumpeter ( 1954 , p. 876, f.n.) also wrote of his habit hiding rather than highlighting his best ideas.
The theme of equilibrium rate of growth also appears in his LSE Lectures (See Young 1990 , p. 45). Demand curve is not constant, as in Marshall, but shifts as a result of the very forces shifting the supply curve. It was therefore more appropriate to have a theory or equilibrium or optimum rate of progress which would keep the supply curve close up to the demand curve. See also Chandra ( 2020 , p. 165).
"Seeking equilibrium conditions under increasing returns is as good as looking for a mare's nest" (Young 1990 , p. 45).
Young (ibid., p. 26) felt that Marshall's supply and demand curves hold ceteris paribus and cannot be integrated to give the whole economic structure or the social picture.
For a discussion of the role of pecuniary external economies vis-a-vis economies of scale in the theory of increasing returns, see Chandra and Sandilands ( 2006 ).
"If consolidation always meant economy, there would be no effective limit to consolidation. That means that competition would inevitably everywhere end in monopoly. But, as we all know, in most fields of economic life competition maintains its health and vigor. This proves that supposed benefits of consolidation are often illusory" (Young 1929d ; Mehrling and Sandilands 1999 , p. 222). At another place Young ( 1990 , pp. 53–54) writes: "Most of the advantages of increasing returns can be had in an industry not consolidated – such, in fact, is the typical modern industry… The advantages of consolidation are not, generally speaking, those of increasing returns. Rather they are those of increased specialization, those of reorganization, not mere increase of size of operating units".
See Machovec ( 1995 , Chapter 10) who points out that both Knight and Chamberlin had a static view of competition while Young subscribed to a process view.
It is interesting to note that Young employed offer curves (or the principle of reciprocal demand) to analyse the growth process though he held money to be central to an economic system. As noted by Blitch ( 1995 , pp. 173–74), perhaps the idea of reciprocal demand (or barter exchange) comes closest to explaining the cumulative process of growth. In offer curves demand and supply are interdependent; along such curves cost of production and marginal utility are released from the pound of ceteris paribus.
"Large production, not large-scale production, permits increasing returns" (Young 1990 , p. 54).
Kaldor ( 1972 , p. 1243) thought that Young's 1928 paper was "so many years ahead of its time that the progress of economic thought passed it by".
Schumpeter ( 1937 , p. 154), for example, writing after his death, stated: "Rarely if ever has fame such as his been acquired on the basis of so little published work".
Young ( 1928b ), in fact, considered any distinction between theoretical and applied economics as largely artificial. He wrote: "Economic theory, divorced from its functional relations to economic problems, or with those relations obscured, is no better than an interesting intellectual game. It gives endless opportunities for dialectical ingenuity. But it cannot advance knowledge, for it leads up a blind alley" (also in Mehrling and Sandilands 1999 , p. 20).
Young ( 1929e ) related the concept of equilibrium with time, and was of the opinion that all economic equilibria are unstable. "Market price is the price which will be found in a given market at a given time. It may be regarded as the limiting form of the short-time or temporary equilibrium price" (also in Young 1990 , p. 154).
Young ( 1990 , pp. 41–43) noted that Marshall's particular expenses curve should not be confused with the supply curve. It was not a true supply curve, but since not all the producers have the same costs, it was upward sloping. Everyone, including those producing at the lowest costs, sell at the equilibrium price, and the area under the curve gives the producers' total expenses. See also Chandra ( 2020 , p. 164).
For Kaldor's interpretation of Smith and Young see Chandra ( 2019 ).
For Currie-Kaldor exchange on increasing returns see Sandilands ( 1990 ), and for Sandilands-Currie-Kaldor exchange see Chandra and Sandilands ( 2020 , 2021 ).
Zurück zum Zitat Blitch, C. (1995). Allyn Young: The Peripatetic Economist. Basingstoke and London: Macmillan Press Ltd. Blitch, C. (1995). Allyn Young: The Peripatetic Economist. Basingstoke and London: Macmillan Press Ltd.
Zurück zum Zitat Buchanan, James M., and Yong J. Yoon. 1999. Generalised increasing returns, Euler's theorem, and competitive equilibrium. History of Political Economy 31 (3): 511–523. CrossRef Buchanan, James M., and Yong J. Yoon. 1999. Generalised increasing returns, Euler's theorem, and competitive equilibrium. History of Political Economy 31 (3): 511–523. CrossRef
Zurück zum Zitat Buchanan, James M., and Yong J. Yoon. 2000. A Smithian perspective on increasing returns. Journal of the History of Economic Thought 22 (1): 43–48. CrossRef Buchanan, James M., and Yong J. Yoon. 2000. A Smithian perspective on increasing returns. Journal of the History of Economic Thought 22 (1): 43–48. CrossRef
Zurück zum Zitat Chamberlin, Edward H. 1933. The Theory of Monopolistic Competition. Cambridge: Harvard University Press. Chamberlin, Edward H. 1933. The Theory of Monopolistic Competition. Cambridge: Harvard University Press.
Zurück zum Zitat Chandra, Ramesh. 2004. Adam Smith, Allyn Young and the division of labour. Journal of Economic Issues 38: 787–805. CrossRef Chandra, Ramesh. 2004. Adam Smith, Allyn Young and the division of labour. Journal of Economic Issues 38: 787–805. CrossRef
Zurück zum Zitat Chandra, Ramesh. 2019. Nicholas Kaldor on Adam Smith and Allyn Young. Review of Political Economy 31 (3): 445–463. CrossRef Chandra, Ramesh. 2019. Nicholas Kaldor on Adam Smith and Allyn Young. Review of Political Economy 31 (3): 445–463. CrossRef
Zurück zum Zitat Chandra, Ramesh. 2020. Allyn Abbott Young. Cham: Palgrave Macmillan. CrossRef Chandra, Ramesh. 2020. Allyn Abbott Young. Cham: Palgrave Macmillan. CrossRef
Zurück zum Zitat Chandra, Ramesh, and Roger J. Sandilands. 2006. The role of pecuniary external economies and economies of scale in the theory of increasing returns. Review of Political Economy 18 (2): 193–208. CrossRef Chandra, Ramesh, and Roger J. Sandilands. 2006. The role of pecuniary external economies and economies of scale in the theory of increasing returns. Review of Political Economy 18 (2): 193–208. CrossRef
Zurück zum Zitat Chandra, Ramesh, and Roger J. Sandilands. 2020. Nicholas Kaldor, Increasing returns and Verdoorn's law. Strathclyde Discussion Papers in Economics, No. 20-02. Glasgow: University of Strathclyde. Chandra, Ramesh, and Roger J. Sandilands. 2020. Nicholas Kaldor, Increasing returns and Verdoorn's law. Strathclyde Discussion Papers in Economics, No. 20-02. Glasgow: University of Strathclyde.
Zurück zum Zitat Chandra, Ramesh, and Roger J. Sandilands. 2021. Nicholas Kaldor, increasing returns and Verdoorn's law. Journal of Post Keynesian Economics 44 (2): 315–339. CrossRef Chandra, Ramesh, and Roger J. Sandilands. 2021. Nicholas Kaldor, increasing returns and Verdoorn's law. Journal of Post Keynesian Economics 44 (2): 315–339. CrossRef
Zurück zum Zitat Clapham, J.M. 1922. On empty economic boxes. Economic Journal 32: 305–314. CrossRef Clapham, J.M. 1922. On empty economic boxes. Economic Journal 32: 305–314. CrossRef
Zurück zum Zitat Colacchio, Georgio. 2005. Reconstructing Allyn A. Young's theory of increasing returns. Journal of the History of Economic Thought 27 (3): 321–344. CrossRef Colacchio, Georgio. 2005. Reconstructing Allyn A. Young's theory of increasing returns. Journal of the History of Economic Thought 27 (3): 321–344. CrossRef
Zurück zum Zitat Ely, Richard T., Thomas S. Adams, Max O. Lorenz, and Allyn A. Young. 1908. Outlines of Economics. New York: The Macmillan Company. Ely, Richard T., Thomas S. Adams, Max O. Lorenz, and Allyn A. Young. 1908. Outlines of Economics. New York: The Macmillan Company.
Zurück zum Zitat Kaldor, Nicholas. 1972. The irrelevance of equilibrium economics. Economic Journal 82: 1237–1255. CrossRef Kaldor, Nicholas. 1972. The irrelevance of equilibrium economics. Economic Journal 82: 1237–1255. CrossRef
Zurück zum Zitat Kaldor, Nicholas. 1975. What is wrong with economic theory. Quarterly Journal of Economics 89 (3): 347–357. CrossRef Kaldor, Nicholas. 1975. What is wrong with economic theory. Quarterly Journal of Economics 89 (3): 347–357. CrossRef
Zurück zum Zitat Knight, Frank H. 1921. Risk, Uncertainty and Profit. Boston: Houghton Mifflin Co. Knight, Frank H. 1921. Risk, Uncertainty and Profit. Boston: Houghton Mifflin Co.
Zurück zum Zitat Krugman, Paul. 1990. Rethinking International Trade. Cambridge and London: MIT Press. CrossRef Krugman, Paul. 1990. Rethinking International Trade. Cambridge and London: MIT Press. CrossRef
Zurück zum Zitat Krugman, Paul. 1993. Toward a counter-counterrevolution in development theory. Annual Conference on Development Economics 1992, 15–38. Washington, DC: World Bank. Krugman, Paul. 1993. Toward a counter-counterrevolution in development theory. Annual Conference on Development Economics 1992, 15–38. Washington, DC: World Bank.
Zurück zum Zitat Laidler, David. 1993. Hawtrey, Harvard, and the origins of the Chicago tradition. Journal of Political Economy 101 (6): 1068–1103. CrossRef Laidler, David. 1993. Hawtrey, Harvard, and the origins of the Chicago tradition. Journal of Political Economy 101 (6): 1068–1103. CrossRef
Zurück zum Zitat Laidler, David. 1999. Fabricating the Keynesian Revolution. Cambridge: Cambridge University Press. Laidler, David. 1999. Fabricating the Keynesian Revolution. Cambridge: Cambridge University Press.
Zurück zum Zitat Machovec, Frank H. 1995. Perfect Competition and the Transformation of Economics. London and New York: Routledge. CrossRef Machovec, Frank H. 1995. Perfect Competition and the Transformation of Economics. London and New York: Routledge. CrossRef
Zurück zum Zitat Mehrling, Perry G. 1996. The monetary thought of Allyn Abbott Young. History of Political Economy 28 (4): 607–632. CrossRef Mehrling, Perry G. 1996. The monetary thought of Allyn Abbott Young. History of Political Economy 28 (4): 607–632. CrossRef
Zurück zum Zitat Mehrling, Perry G. 1997. The Money Interest and the Public Interest. Cambridge and London: Harvard University Press. Mehrling, Perry G. 1997. The Money Interest and the Public Interest. Cambridge and London: Harvard University Press.
Zurück zum Zitat Mehrling, Perry G., and Roger J. Sandilands. 1999. Money and Growth: Selected Papers of Allyn Abbott Young. London and New York: Routledge. CrossRef Mehrling, Perry G., and Roger J. Sandilands. 1999. Money and Growth: Selected Papers of Allyn Abbott Young. London and New York: Routledge. CrossRef
Zurück zum Zitat Perala, Maiju. 2006. Looking at the other side of the coin: Allyn Young and the early development theory. Journal of the History of Economic Thought 28 (4): 461–488. CrossRef Perala, Maiju. 2006. Looking at the other side of the coin: Allyn Young and the early development theory. Journal of the History of Economic Thought 28 (4): 461–488. CrossRef
Zurück zum Zitat Raffaelli, Tiziano. 2003. Marshall's Evolutionary Economics. Abingdon and New York: Routledge. CrossRef Raffaelli, Tiziano. 2003. Marshall's Evolutionary Economics. Abingdon and New York: Routledge. CrossRef
Zurück zum Zitat Robbins, Lionel. 1932. An Essay on the Nature and Significance of Economic Science. London: Macmillan. Robbins, Lionel. 1932. An Essay on the Nature and Significance of Economic Science. London: Macmillan.
Zurück zum Zitat Romer, Paul M. 1986. Increasing returns and long-run growth. Journal of Political Economy 94: 1002–1038. CrossRef Romer, Paul M. 1986. Increasing returns and long-run growth. Journal of Political Economy 94: 1002–1038. CrossRef
Zurück zum Zitat Romer, Paul M. 1989. Capital accumulation in the theory of long-run growth. In Modern Business Cycle Theory, ed. Robert J. Barro. Cambridge: Harvard University Press. Romer, Paul M. 1989. Capital accumulation in the theory of long-run growth. In Modern Business Cycle Theory, ed. Robert J. Barro. Cambridge: Harvard University Press.
Zurück zum Zitat Rosenstein-Rodan, Paul. 1943. Problems of industrialisation in eastern and south-eastern Europe. Economic Journal 53: 202–211. CrossRef Rosenstein-Rodan, Paul. 1943. Problems of industrialisation in eastern and south-eastern Europe. Economic Journal 53: 202–211. CrossRef
Zurück zum Zitat Rosenstein-Rodan, Paul. 1961. Notes on the theory of the big push. In Economic Development of Latin America, ed. H.S. Ellis and H.C. Wallaich. London: Macmillan. Rosenstein-Rodan, Paul. 1961. Notes on the theory of the big push. In Economic Development of Latin America, ed. H.S. Ellis and H.C. Wallaich. London: Macmillan.
Zurück zum Zitat Sandilands, Roger J. 1990. The Life and Political Economy of Lauchlin Currie. Durham and London: Duke University Press. CrossRef Sandilands, Roger J. 1990. The Life and Political Economy of Lauchlin Currie. Durham and London: Duke University Press. CrossRef
Zurück zum Zitat Sandilands, Roger J. 2000. Perspectives on Allyn Young in theories of endogenous growth. Journal of the History of Economic Thought 22 (3): 309–328. CrossRef Sandilands, Roger J. 2000. Perspectives on Allyn Young in theories of endogenous growth. Journal of the History of Economic Thought 22 (3): 309–328. CrossRef
Zurück zum Zitat Schumpeter, Joseph A. 1937. Allyn A. Young. In Encyclopedia of Social Sciences, 514–515.New York: Macmillan. Schumpeter, Joseph A. 1937. Allyn A. Young. In Encyclopedia of Social Sciences, 514–515.New York: Macmillan.
Zurück zum Zitat Scitovsky, Tibor. 1954. Two concepts of external economies. Journal of Political Economy 62: 143–151. CrossRef Scitovsky, Tibor. 1954. Two concepts of external economies. Journal of Political Economy 62: 143–151. CrossRef
Zurück zum Zitat Sraffa, Piero. 1926. The laws of return under competitive conditions. Economic Journal 6: 535–550. CrossRef Sraffa, Piero. 1926. The laws of return under competitive conditions. Economic Journal 6: 535–550. CrossRef
Zurück zum Zitat Young, Allyn. 1913. Pigou's wealth and welfare. Quarterly Journal of Economics 27 (4): 672–686. Reprinted in Mehrling and Sandilands (1999, pp. 86–96). Young, Allyn. 1913. Pigou's wealth and welfare. Quarterly Journal of Economics 27 (4): 672–686. Reprinted in Mehrling and Sandilands (1999, pp. 86–96).
Zurück zum Zitat Young, Allyn. 1928a. Increasing returns and economic progress. Economic Journal 38 (152): 527–542. Reprinted in Mehrling and Sandilands (1999, pp. 49–61). Young, Allyn. 1928a. Increasing returns and economic progress. Economic Journal 38 (152): 527–542. Reprinted in Mehrling and Sandilands (1999, pp. 49–61).
Zurück zum Zitat Young, Allyn. 1928b. English political economy. Economica 8 (22): 1–15. Reprinted in Mehrling and Sandilands (1999, pp. 15–28). Young, Allyn. 1928b. English political economy. Economica 8 (22): 1–15. Reprinted in Mehrling and Sandilands (1999, pp. 15–28).
Zurück zum Zitat Young, Allyn. 1929a. The creator of wealth. In The Book of Popular Science, 110–116. New York: The Grolier Society. Reprinted in Mehrling and Sandilands (1999, pp. 149–152). Young, Allyn. 1929a. The creator of wealth. In The Book of Popular Science, 110–116. New York: The Grolier Society. Reprinted in Mehrling and Sandilands (1999, pp. 149–152).
Zurück zum Zitat Young, Allyn. 1929b. Supply and demand. In Encyclopaedia Britannica 1928, 579–580. London: The Encyclopaedia Britannica Company. Reprinted in Mehrling and Sandilands (1999, pp. 143–146). Young, Allyn. 1929b. Supply and demand. In Encyclopaedia Britannica 1928, 579–580. London: The Encyclopaedia Britannica Company. Reprinted in Mehrling and Sandilands (1999, pp. 143–146).
Zurück zum Zitat Young, Allyn. 1929c. Big business: How the economic system grows and evolves like a living organism. In The Book of Popular Science, 5387–5394. New York: The Grolier Society. Reprinted in Mehrling and Sandilands (1999, pp. 411–420). Young, Allyn. 1929c. Big business: How the economic system grows and evolves like a living organism. In The Book of Popular Science, 5387–5394. New York: The Grolier Society. Reprinted in Mehrling and Sandilands (1999, pp. 411–420).
Zurück zum Zitat Young, Allyn. 1929d. The making of wealth. In The Book of Popular Science, 3377–3385. New York: The Grolier Society. Reprinted in Mehrling and Sandilands (1999, pp. 220–230). Young, Allyn. 1929d. The making of wealth. In The Book of Popular Science, 3377–3385. New York: The Grolier Society. Reprinted in Mehrling and Sandilands (1999, pp. 220–230).
Zurück zum Zitat Young, Allyn. 1929e. Price. In Encyclopedia Britannica 1928. London: The Encyclopedia Britannica Company. Young, Allyn. 1929e. Price. In Encyclopedia Britannica 1928. London: The Encyclopedia Britannica Company.
Zurück zum Zitat Young, Allyn. 1990. Nicholas Kaldor's notes on Allyn Young's LSE lectures 1927–29. Journal of Economic Studies 17 (3/4): 18–114. Young, Allyn. 1990. Nicholas Kaldor's notes on Allyn Young's LSE lectures 1927–29. Journal of Economic Studies 17 (3/4): 18–114.
Allyn Young on Increasing Returns


text:
Casting updates for Chichester's 2014 Festival season
Anne Cox | May 13, 2014 | News | No Comments
Latest casting has been announced for some of this season's productions at Chichester Festival Theatre.
Joining Rupert Everett in the big hitter, Amadeus, will be Joshua McGuire who will play Mozart.
McGuire is currently starring in the highly praised Privacy at the Donmar Warehouse. He has also appeared in Posh, at the Royal Court and West End, the title role in Hamlet at Shakespeare's Globe and BBC 2's The Hour.
The production also features Jessie Buckley (Henry V) as Constanze Weber and stage and screen veteran John Standing Count Franz Orsini-Rosenberg.
The cast also includes Simon Jones as Joseph II Emperor of Austria, Richard Clifford as Baron Gottfried van Swieten and Timothy Kightley as Count Johann Killian von Strack.
Amadeus will be the first production staged in the newly refurbished Festival Theatre, which reopens this summer following a £22 million redevelopment.
The major improvements to the Grade II listed building will include a transformed auditorium, increased seating capacity of 1,300, more spacious foyer areas with new cafés, bars and outdoor terraces, as well as improved and expanded behind-the-scenes facilities.
Paulette Randall (Fences) will direct Frankie & Johnny in the Clair De Lune.
CFT regular Jonathan Coy joins Mike Grady, Paul Ready and Marcia Warren in the cast of Miss Julie / Black Comedy.
Miss Julie / Black Comedy is at the Minerva Theatre, 4 July – 9 August.
Amadeus is at the Festival Theatre from 12 July – 2 August.
Frankie & Johnny in the Clair De Lune is at the Minerva Theatre, 6 November – 6 December.
An Ideal Husband is at the Festival Theatre, 21 November – 13 December.
National Theatre's Angels In America wings it to Broadway
It's A Wonderful Life launches new theatre for South London
Theatre Uncut puts politics on the international stage
Provocative Fringe hit, The Curing Room, in London transfer


harry and hermioen friendship third साल. friendship. HD Wallpaper and background images in the Harry Potter फिल्में club tagged: harry hermione.
This Harry Potter फिल्में photo might contain महिमा छेद, ऐंबुलेंस, लेज़्रेटो, ग्लोरी होल, lazaretto, आग, and नरक.


text:
Charles Dundas, 1. Baron Amesbury (* 5. August 1751; † 30. Juni 1832 in Pimlico, London) war ein britischer Politiker.

Leben
Charles Dundas war das jüngste der sechs Kinder von Thomas Dundas (um 1708–1786) und dessen Ehefrau Lady Janet Maitland, Tochter des Charles Maitland, 6. Earl of Lauderdale. Sein älterer Bruder war Thomas Dundas. Dundas besuchte die Edinburgh High School und studierte dann Rechtswissenschaften an der Universität Edinburgh und am Trinity College der Universität Cambridge. 1774 trat er dem Middle Temple bei und wurde drei Jahre später zum Barrister berufen.

Am 16. Februar 1782 heiratete er Anne Whitley († 1812), Erbtochter des Ralph Whitley, Gutsherr von Kintbury und Aston Hall in Berkshire. Mit ihr hatte er eine Tochter, Hon. Janet Dundas (um 1783–1846). 1822 heiratete Dundas ein zweites Mal, diesmal seine Cousine Margaret Barclay-Maitland († 1841).

1797 wurde er im Rang eines Colonel Kommandeur der White Horse Volunteer Cavalry, der Landwehrkavallerie von Berkshire.

Politischer Werdegang
Er gehörte der Partei der Whigs an und wurde bei den Unterhauswahlen 1775 als Abgeordneter für das Borough Richmond in Yorkshire ins britische Unterhaus gewählt. Sein Onkel Sir Lawrence Dundas, 1. Baronet war seit 1766 Eigentümer der Orkneys und Shetlands. Zunächst sein Vater und schließlich sein Bruder Thomas hielten seit 1768 das Unterhausmandat für das County Orkney and Shetland. Auf Grund seiner militärischen Verpflichtungen konnte letzterer seine Aufgaben nicht mehr wahrnehmen, weshalb Lawrence Dundas bei den Unterhauswahlen 1780 seinen Neffen Charles Dundas für Orkney and Shetland antreten ließ. Dundas unterlag jedoch dem durch die Regierung gestützten Robert Baikie. Durch eine Petition im Februar des folgenden Jahres, wurde Dundas jedoch nachträglich als Vertreter von Orkney and Shetland eingesetzt.

1784 trat Dundas erneut für das Borough Richmond an und hielt das Mandat bis 1786. In diesem Jahr wurde er als Knight of the Shire für Berkshire gewählt. Für dieses County saß er bis zu seiner Erhebung in den Adelsstand 1832 im Unterhaus. Zwischen 1785 und 1820 war Dundas Berater des Kronprinzen und späteren britischen Königs Georg IV., der in diesem Zeitraum als Duke of Rothesay das Amt des Great Steward of Scotland innehatte.

Am 16. Mai 1832 wurde er zum Baron Amesbury, of Kintbury, Amesbury, and Barton-court, in the County of Berks, and of Aston-hall in the County of Flint erhoben, und wurde dadurch Mitglied des britischen Oberhauses. Dundas verstarb am 30. Juni desselben Jahres. Da er keine männlichen Nachkommen hatte, erlosch der Titel mit seinem Tod. Seine Ländereien und sein Vermögen fielen an seine Tochter Janet bzw. deren Ehemann, den Unterhausabgeordneten und späteren Admiral Sir James Deans (1785–1862), der 1808 anlässlich der Eheschließung mit Janet den Familiennamen "Whitley-Deans-Dundas" angenommen hatte.

Literatur
 George Edward Cokayne, Vicary Gibbs (Hrsg.): The Complete Peerage. Band 1, The St Catherine Press, London 1910, S. 120–121 (archive.org).
 David R. Fisher: Dundas, Charles (1751–1832), of Barton Court, Kintbury, Berks.; Aston Hall, Hawarden, Flints., and Manor House, Pimlico, Mdx. In: David R. Fisher (Hrsg.): The History of Parliament. The House of Commons 1820–1832. Cambridge University Press, Cambridge 2009 (Online)
 Francis Watt: Dundas, Charles, Baron Amesbury. In: H.C.G. Matthew (Hrsg.): Dictionary of National Biography. Oxford University Press, Oxford 1995 (Online bei thepeerage.com).

Weblinks

Einzelnachweise

Amesbury, Charles Dundas, 01 Baron
Charles, 01 Baron Amesbury
Abgeordneter des House of Commons (Vereinigtes Königreich)
Mitglied des House of Lords
Politiker (Orkney)
Politiker (Shetlandinseln)
Politiker (18. Jahrhundert)
Politiker (19. Jahrhundert)
Mitglied der Whig Party
Oberst (British Army)
Schotte
Brite
Geboren 1751
Gestorben 1832
Mann