I have been obsessed with fashion since I can remember. When I was younger there was no question who had the best dressed barbies in my hood. I had store bought barbie clothes, ones my Nana had made for my mother's barbies when she was young, and ones crafted by my Nana per my request. I was picky about my own style, too. My mom swears I wore nothing but dresses until almost 2nd grade and you better believe I always had a purse and matching accessories for every ensemble.
When it comes to style, you either have it or you don't. I have had my theories over the years while analyzing other's fashion, at times wondering if they own a full length mirror, but I try not to be too harsh. I've noticed some people can't let go of certain trends or it's obvious they're stuck in a specific era. I also think some people just stick with what they think works for them and the list goes on and on.
When it comes to fashion we all know trends are created all the time and it's okay to occasionally style yourself with the newest one but just remember; all good trends come to an end. With that being said I would like to share a piece of advice given to me by my Nana many years ago. My Nana on e told me, "When you're shopping for clothes ask yourself these questions: Did this look good 50 years ago? Will it look good 50 years from now?" If you find yourself answering no then it may not be the best purchase to make however, if the answer is yes then it's a classic so throw it in the bag!
The other day I stopped into my family's antique store, Mayberry Antique Mall located on Broadway in Mayfield, Kentucky. As soon as I walked in I noticed 3 huge piles of vintage clothing that had just been added to the inventory. I stopped dropped and started digging! I found 6 ensembles from the 1960's and 70's to add to my collection of classic wardrobe pieces.
The first being this darling pleaded floral dress. I added the yellow belt and can totally see this as a fall piece come fall time by wearing some boots instead of these wedges.
This dress is beautiful and fit me perfectly! I can see this being perfect for a shower or an afternoon social event.
I am obsessed with this darling plaid midi dress (that has pockets)! I added the belt from my collection and can see this dress being even cuter in the fall with some boots and a big hat!
I'm obsessed with this darling little top! I paired with a pair of flare jeans but I think it could work with some skinny jeans too. This reminds me of something my mother would have worn in the 70's. I didn't incorporate jewelry for this photo shoot but I know some big colorful earrings (maybe yellow) would totally make this ensemble!
This top got paired with some flare jeans as well. It's a classic floral blouse from the 70's as well. I'm sorry I don't have a shot of front (opps) but there's a picture to display the band that goes across the bottom.
The 6th and final vintage piece I found was the top I am wearing in the photos above. I knew I had to have it and the exact skirt at home I would pair it with. I would also not be opposed to pairing this top with a pair of black trousers. It's so sophisticated.
Love that plaid dress. I like timeless piece, never go outraged.
Sorry for the typo. Should be outdated.
Thank you, I will definitely visit your site!


text:
Notable attendees and absentees at Paris Summit
May 12, 2019 Venkat Raman
No Shows include Trump, Zuckerberg and Sandberg
Wellington, May 12, 2019
French President Emmanuel Macron and Prime Minister Jacinda Ardern (AFP Photo)
There will be notable absences at a summit to crackdown on social media being used for terrorism in Paris this week.
French President Emmanuel Macron and Prime Minister Jacinda Ardern earlier announced a joint effort to work toward curbing terrorism on social media.
Neither Mark Zuckerberg nor Sheryl Sandberg from Facebook are attending, nor is the United States President Donald Trump.
Leaders and Agenda
On Wednesday, May 15, 2019, the Heads of State from Canada, the United Kingdom, Ireland, Norway, Jordan, Senegal, Indonesia and the European Union will descend on Paris for the Christchurch Call summit chaired by Prime Minister Jacinda Ardern and French President Emmanuel Macron.
There the world leaders will sign an Agreement on how to try to end the use of social media to organise and promote terrorism.
It comes after 51 people died in Christchurch as the result of a terrorist attack that was livestreamed on Facebook.
Technology Representatives
Technology representatives including Twitter's CEO Jack Dorsey, Former Deputy British Prime Minister Nick Clegg on behalf of Facebook and Microsoft President Brad Smith will attend. Google, Amazon and Wikimedia will also be there.
Nick Clegg, who is Facebook's Head of Global Policy and Communication, will be in Paris for the summit.
Ms Ardern said that her conversations with Mr Zuckerberg had been "really consistent and high level'' but "of course" she would have preferred for him to attend the Summit in person.
While she hasn't spoken directly to Mr Trump about the Christchurch Call, she said she had "no qualms promoting to any world leader'' the terrorism issues arising out of the use of social media.
The list of attendees includes European Commission President Jean-Claude Juncker, French President Emmanuel Macron, Canada Prime Minister Justin Trudeau, UK Prime Minister Theresa May, Irish Prime Minister (Taoiseach) Leo Varadkar, Norway Prime Minister Erna Solberg, Jordan King Abdullah II bin Al-Hussein, Senegal President Macky Sall, Indonesia Vice President Jusuf Kalla, Twitter Chief Executive Jack Dorsey.
Gratitude to Canada
Meanwhile, New Zealand High Commissioner in Ottawa Daniel Mellsop has thanked Canada publicly for condemning online extremist material.
He said that Canada's Quebec Province, where six Muslim worshippers were killed by a gunman in January 2017, has also helped with advice for New Zealand after the 51 killings in Christchurch.
Jo Moir is a Political Reporter at Radio New Zealand. The above story and pictures have been published under a Special Arrangement with www.rnz.co.nz
Fiji mulls new policies to boost economic development
Sports, Culture and business on Sangam agenda

redpajama_set_name: RedPajamaCommonCrawl


Besides play more than 30 million Spotify music, the Spotify App can also allow you to play your local music files stored on your computer, but the supported music files are only limited to �... The Local Files pane opens. Select the track whose information you want to change. To edit a selection of tracks at the same time, rather than just one, select the first track, and then Shift-click the last track.
What DRM does Spotify and other music services use to protect the files that users cache on their phones? Is there any way to convert Spotify music files to MP3s? Does Spotify pay royalties for music that I play from my Local Files in the Spotify desktop client?... The Local Files pane opens. Select the track whose information you want to change. To edit a selection of tracks at the same time, rather than just one, select the first track, and then Shift-click the last track.
Time to add the mixtape/album to your Spotify. Open up Spotify in your computer, then click the top left "Edit" button, then "Preferences...' at the bottom. You'll be taken to settings, where you can scroll down to local files.... Time to add the mixtape/album to your Spotify. Open up Spotify in your computer, then click the top left "Edit" button, then "Preferences...' at the bottom. You'll be taken to settings, where you can scroll down to local files.


text:
Queen's 'Greatest Hits': The UK's Bestselling Artist Album Of All Time
November 14, 1981 saw the chart debut of the compilation that is officially the UK's bestselling album of all time.
Queen artwork: UMG
The record that is recognized by the Official Charts Company as the UK's bestselling artist album of all time, Queen's first Greatest Hits collection, began its reign on November 14, 1981. It's gone on to sell more than 6.1 million copies in the UK alone.
A little over seven and a half years into the band's chart lifespan, and with their "Under Pressure" collaboration with David Bowie just seven days away from becoming Queen's second chart-topping single, the retrospective gave them their third No.1 UK album.
Double Cream: Eric, Ginger And Jack At The Crossroads In A White Room
Best Martin Luther King Songs: 10 Moving Tributes
In 2014, the OCC confirmed that Greatest Hits had become the first album to pass sales in Britain of six million copies, some 900,000 copies ahead, to February that year, of ABBA's Gold anthology. To add to the honor, the Greatest Hits II album, released exactly a decade later in October 1991, just a month before the untimely death of Freddie Mercury, stood at No.10 in that all-time sales list, with a further 3.9 million UK sales.
That first Greatest Hits, seen by many as the definitive hits collection by any artist, had by November 2021 risen to an astonishing aggregate of 971 weeks in the weekly UK Top 100, 291 of them spent inside the Top 40 and 131 in the Top 20.
Now certified eight times platinum in Queen's home country, the record also topped the charts on first release in Germany, Holland and Austria among other markets. The album appeared on the UK chart for the first time at No.2, behind British rock'n'roller Shakin' Stevens' Shaky, before starting the first of four weeks at the top.
The hits package was in the Top 5 every week until the end of January 1982 and remained in the Top 75 for almost a year, until the end of September. It has reappeared at regular intervals ever since, and as Queen's legend grew, the album spent an unbroken run of 224 weeks on the Top 100 between 1983 and 1987.
Listen to the best of Queen on Apple Music and Spotify.
Mercury's sad death did, of course, give Greatest Hits new momentum, with a nine-week sequence in the Top 10 from late 1991 alongside its second volume. The success of the Bohemian Rhapsody film and its soundtrack propelled the collection back into the UK Top 30 once again, and it spent much of 2020 securely in the Top 20, and sometimes higher. Few, if any, albums in the history of British music have been quite so indispensable.
Buy or stream Queen's Greatest Hits.
Related Topics:Classic PopDavid BowieFreddie MercuryOn This DayQueenRock
I have some of your vinyls in 331/3
I have lots of albums from all years!! Over 1500 in all. Queen, beatles, meatloaf, zz-top, led zepplin, gary puckett and the union gap, omg so many!!!
FIRST CONCERT I EVER ATTENDED WAS IN 1978 IN CLEVELAND……..FELL IN LOVE WITH THE BAND….LOVED FREDDIE……..HATED FOR HIM TO DIE……..MISS HIM……MY DAUGHTER WENT TO SEE THEM RECENTLY WITH ADAM LAMBERT……THOUGHT THE SHOW WAS WONDERFUL !
FIRST CONCERT I EVER ATTENDED WAS IN 1978 IN CLEVELAND……..FELL IN LOVE WITH THE BAND….LOVED FREDDIE……..HATED FOR HIM TO DIE……..MISS HIM……MY DAUGHTER WENT TO SEE THEM RECENTLY WITH ADAM LAMBERT……THOUGHT THE SHOW WAS WONDERFUL !SUZI IS NOT ME !
BBC Radio Janice Long Tribute To Feature Paul McCartney, Elvis Costello & More
Donna Missal Announces EP 'In The Mirror, In The Night,' Shares New Single 'Insecure'
Elvis Costello Shares Stories Behind 'The Boy Named If' On 'World Café'
'Prophets Seers & Sages' & 'My People…': T. Rex's Four-Year Run To No.1
How Well Do You Know Paul McCartney's 'Flaming Pie'?
How Toronto Became The Epicenter Of Cool For Canada's Summer Of Love


We understand that not everyone works the same way.
Because of this, we've designed Cashboard to be as flexible as possible.
To help you get started we've gathered some articles that should get you going in the right direction.
Which of these describes your work style?
I just want to send invoices!


What exactly is Sun vs. Snow? Well, it's an annual contest Michelle Hauck and I host every year to connect writers to agents looking for great manuscripts.
Michelle and I have a date where we accept submissions (2018 = January 23). We take 200 entries and then close the window. (In 2016, it took us four minutes to get to this number!) Once all entries have been received, Michelle and I read through each one and select 16 separate picks. The contest is open to all MG, YA, NA and Adult genres (excluding erotica).
After our entries have been selected, we post our teams on our corresponding blogs (Michelle's blog is here). I'm Team Sun because I live in Arizona, while Michelle is Team Snow because she resides in Indiana.
Once our announcement post goes up, the selected entries work with mentors to polish their query and first page. Then a week or two later, we invite literary agents to both our blogs to review the shiny new entries and make requests if they are so inclined.
Last year, we had over 100 requests from participating agents and we are hoping for more in 2018.
If you're interested in more details about the contest, or rules for entering, please go here.


text:
Q: AccessControlException createClassLoader I'm using custom class loader and the Microsoft JDBC driver in my applet. When my class loader calls super() in the body of its constructor (creates new instance of class loader) I get

java.security.AccessControlException: access denied
  ("java.lang.RuntimePermission" "createClassLoader")

Now, I have tried running applet through jnlp
<param name="jnlp_href" value="permissions.jnlp">

with
<security>
<all-permissions/>
</security>

...but the browser just freezes to death.
Later, I have signed the JAR containing my applet, and I still get

java.security.AccessControlException

Is there a solution to this? Please, don't suggest
grant {
  permission java.security.AllPermission;
};

It's a heavy, not user-friendly solution.

redpajama_set_name: RedPajamaStackExchange


The Crankworx Europe 2014 weekend is almost at an end, but it's been immense. Anthony Messere stormed his way to the win in Slopestyle and Brendan Fairclough took the win in the Pump Track Challenge yesterday, make sure you check out the full results from the slopestyle event. Ben Winder was on hand to capture the action from Slopestyle and the Pump Track, check it out below.
This afternoon will see the Polygon Air Downhill race, and as part of Crankworx it will combine round 4 of the iXS European Downhill Cup. Check out the course preview with Brendog and Gee Atherton.
If you've missed them, you can check out our galleries from Cranworx Europe below!


text:
False df44278b-4752-47f6-8f45-98010e24eaa7
GAM launches sustainable local emerging bond strategy; first in new range of ESG focused strategies
GAM Investments today announces the launch of a sustainable local emerging market bond strategy. The new investment approach was developed in close partnership with VBV-Pensionskasse, a leading pension fund for sustainable investments in Austria. Due to a longstanding existing partnership with GAM in this asset class, VBV has moved a three-digit million investment into the new sustainable solution. The strategy will be managed by GAM's emerging markets debt team and is the first in a range of sustainable investment strategies the firm plans to launch in 2021.
The new strategy draws upon the expertise of Paul McNamara and GAM's highly experienced emerging markets debt team, whose differentiated, conviction-driven approach to EM debt investing has been developed over 20 years. The strategy seeks to generate long-term financial returns by investing in a way that is sensitive to the impact decision making may have on society and the environment.
The approach combines a positive tilt towards sovereigns with higher environmental, social and governance (ESG) scores, as defined by its benchmark, the JP Morgan ESG GBI-EM GD Index, with the team's proprietary investment process incorporating ESG factors for active allocation within the index tilts. The JP Morgan ESG GBI-EM GD Index leverages research from both Sustainalytics and RepRisk therefore allowing investors to combine the benefits offered by active management applied to an ESG benchmark.
The team's process mirrors that of the long-running local emerging bond strategy. Based on its assessment of developments in the 'Big 3' global economies – the US, Europe and China – the team establishes three to five top-down global themes, which determine country selection, along with specific return and risk driver preferences. Given the team's emphasis on crisis avoidance, country analysis is then performed using the team's proprietary 'Crisis Cycle Filter' (see notes to editors). This captures the interaction between core ESG factors and nine traditional macroeconomic variables considered to be highly reliable, early indicators of financial crises, such as falling FX reserves or rapidly rising inflation.
The strategy typically has active exposure to 15-25 emerging and frontier markets, centred upon approximately 10 very liquid core markets and 100-150 bonds and FX forwards.
Paul McNamara, Investment Director for emerging market debt at GAM, said: "We have taken ESG factors into account in our investment process for our local emerging bond strategy for a number of years, purely for their impact on risk-adjusted returns. However, as ESG factors become more efficiently priced in the sovereign debt market, we believe that now is the time for a strategy that targets both a specific ESG tilt and integrates ESG factors from a risk/return perspective."
Günther Schiendl, Member of the Executive Board of VBV-Pensionskasse, said: "VBV-Pensionskasse invests responsibly, sustainably and with a focus on performance. Particularly in the area of fixed-income emerging markets, a new approach that increasingly takes ESG criteria into account was important to us, as this type of solution has been rare so far."
Stephanie Maier, Global Head of Sustainable and Impact Investment at GAM, said: "At GAM, we are listening to the clear client demand for more strategies focused on sustainable investing and are delighted to be working in partnership with our clients to develop these. The sustainable local emerging bond strategy combines the benefits of using a well-established ESG benchmark, with the opportunity to benefit from active management and expertise of GAM's emerging markets bond team. Later this year, we plan to launch additional ESG focused products, further building on our award winning Swiss Sustainable Companies strategy1, which has a track record of more than 20 years."
1 The GAM Swiss Sustainable Companies strategy won the "2020 Best Swiss Equities" award at the Swiss Sustainable Funds Awards on 9 June 2020. The award recognises an investment strategy, which has demonstrated a social and environmental approach focused on capital preservation and performance and which has demonstrated a clear commitment to future generations. No fee was paid to be considered for this award.
GAM's Crisis Cycle Filter
The investment team believes that the impact of ESG factors on the creditworthiness of governments is best understood in a framework that captures the interaction between ESG factors and traditional macro variables. The Crisis Cycle Filter analyses nine variables, which include falling foreign exchange reserves; falling ratio of FX reserves to broad money; zero or negative real interest rates; rapidly rising inflation; rapid rise in credit/GDP ratio; high and rising current account deficit/exports; uncompetitive exchange rate; vulnerable banking sector (qualitative); and rapid deterioration in fiscal position (qualitative).
The underlying philosophy of the framework is that none of these variables on their own are likely to trigger a crisis. If an economy suffers from weaknesses in many of these areas, however, the authorities have typically lost the degrees of freedom required for a coherent policy response to a negative shock, and a crisis is consequently far more likely. Similarly, weaknesses in ESG are more likely to be costly to returns when the economy suffers from other macroeconomic imbalances. Via the Crisis Cycle Filter, ESG factors play a role in both risk management and potentially enhancing returns.
Charles Naylor
Global Head of Communications and Investor Relations
Jessica Grassi
Ute Dehn Christen
T+41 58 426 31 36 Media Relations
Kathryn Jacques
Follow us on: Twitter, LinkedIn
GAM is a leading independent, pure-play asset manager. The company provides active investment solutions and products for institutions, financial intermediaries and private investors. The core investment business is complemented by private labelling services, which include management company and other support services to third-party asset managers. GAM employed 701 FTEs in 14 countries with investment centres in London, Cambridge, Zurich, Hong Kong, New York, Milan and Lugano as at 31 December 2020. The investment managers are supported by an extensive global distribution network. Headquartered in Zurich, GAM is listed on the SIX Swiss Exchange with the symbol 'GAM'. The Group has AuM of CHF 122 billion (USD 138.0 billion) as at 31 December 2020.
The information in this document is given for information purposes only and does not qualify as investment advice. Opinions and assessments contained in this document may change and reflect the point of view of GAM in the current economic environment. No liability shall be accepted for the accuracy and completeness of the information. Past performance is no indicator for the current or future development. All financial investments involve an element of risk. Therefore, the value of the investment and the income from it will vary and the initial investment amount cannot be guaranteed. There is no guarantee projections will be realised. Allocations and holdings are subject to change.
Awards may not be representative of any one client's experience and is not indicative of the future performance of a GAM product or GAM.
The J.P. Morgan ESG GBI-EM Global Diversified (JESG GBI-EM) tracks the performance of bonds issued by emerging market governments and denominated in the local currency of the issuer. The index applies an Environmental, Social and Governance (ESG) scoring and screening methodology to tilt toward issuers ranked higher on ESG criteria and green bond issues, and to underweight or remove issuers that rank lower. The JESG GBI-EM is based on the established flagship J.P. Morgan GBI-EM Global Diversified Index. The returns and statistics are available since December 2012.
Download pdf (EN) Download pdf (DE) Download pdf (IT) Download pdf (FR)


Find great deals on ebay for eaton square fabric. Luxury curtain and upholstery fabric. Fabric width minus 1 10 square table topper 35 x 35 10 quantity.
Its made of 100 cotton for superior comfort.
Eaton square upholstery fabric. Jan 25 2019 home decor 8x 8 fabric swatch upholstery eaton square mckenna coral opticalhome decor 8x 8 fabric swatch upholstery eaton square mckenna coral optical at joann fabric. Find the right upholstery products for your next project and lets create something together. Luxury curtain and upholstery fabric. Items in search results 8 yards of 14 cord trim eaton square w tape cedar creek birch.
This 54 inch wide light upholstery fabric is perfect for headboards window coverings bedding and even pillows. Eaton square velvet list 1 curtain fabric in amethyst by bill beaumont textiles by the metre. Heavy upholstery horizontal repeat. Traditional and oversized floral bouquets colored in an updated color story on the eaton square upholstery fabric coral floral will bring a pop of color to any room.
Save eaton square fabric to get e mail alerts and updates on your ebay feed. 55 inches fabric type. Images should be used as a guide only. We are the brand designers depend on for everyday decorating.
Results 1 24 of 1980 fabrics upholstery furniture. Eaton square fabric by bill beaumont textiles suitable for curtains and upholstery. 63 rayon 21 polyester 16 linen width. Eaton square 100 polyester.
Also available with our exclusive made to measure curtain service. Fabricut is one of the largest distributors of decorative fabrics and wholesale fabric. Eaton square upholstery fabric 54 swirl sonoma details httpwww. 0 inches vertical repeat.
Thousands to choose from and sold by the yard. Due to screen restrictions it is impossible to 100 accurately display a colour. Please note this product has been made to your exact requirements and therefore we cannot accept returned goods on bespoke products.


text:
The West Indian cricket team that toured England in the 1928 season was the first to play Test cricket. The team was not very successful, losing all three Tests by an innings and winning only five of the 30 first-class matches played.

The background to the tour
In 1926, the Imperial Cricket Conference, forerunner of the International Cricket Council, allowed for the first time delegates from India, New Zealand and the West Indies to attend. The three were invited to organise themselves into cricket boards that could, in future, select representative teams to take part in Test matches, which had hitherto been restricted to sides from England, Australia and South Africa.

The West Indian cricket tour of England in 1928 was the first of these new Test-playing ventures, and it was backed heavily by the cricket establishment because of the success of the 1923 West Indian cricket team in England, when the side won 12 matches.

The West Indies touring team
The West Indies team had 17 members, four each from Jamaica, Trinidad (now Trinidad and Tobago) and British Guiana (now Guyana) and five from Barbados. The side was captained by Karl Nunes, who had been vice-captain of the 1923 touring side.

The players were:
 Karl Nunes (Jamaica), captain
 Vibart Wight (British Guiana), vice-captain
 Edward Bartlett (Barbados)
 Cyril Browne (British Guiana)
 George Challenor (Barbados)
 Learie Constantine (Trinidad)
 Maurice Fernandes (British Guiana)
 George Francis (Barbados)
 Herman Griffith (Barbados)
 Teddy Hoad (Barbados)
 Freddie Martin (Jamaica)
 James Neblett (British Guiana)
 Ernest Rae (Jamaica)
 Clifford Roach (Trinidad)
 Wilton St Hill (Trinidad)
 Tommy Scott (Jamaica)
 Joe Small (Trinidad)

All of the 17 players with the exceptions of Neblett and Rae played in at least one of the three Test matches: Neblett later played once for West Indies against England in 1934-35, but Rae, the father of the later Test cricketer Allan Rae, never played in a Test match.

Seven of the 17 – Nunes, Browne, Challenor, Constantine, Fernandes, Francis and Small – had toured with the 1923 touring team, but five years on the batsmen Challenor, Fernandes and Small were unable to repeat their success of 1923, and Francis and Browne were less successful in bowling. Lack of a frontline wicketkeeper – George Dewhurst from the 1923 team did not play in 1928 – meant that Nunes was the main wicketkeeper.

Test series
Three Test matches were arranged, each of three days' duration.

1st Test

Jack Hobbs being unfit, England, on winning the toss, opened with Herbert Sutcliffe and Charlie Hallows, and the three West Indian fast bowlers, Constantine, Griffith and Francis, plus Browne, caused early discomfort. But though wickets fell fairly regularly, runs were also scored very fast. Ernest Tyldesley made 122 and captain Percy Chapman 50, and at the end of the first day England had reached 382 for eight. Constantine, who finished with four for 82, ended the innings quickly on the second morning. West Indies, opening with Martin and Challenor, reached 70 without loss by lunchtime, and the partnership went to 86 before Martin was out to Maurice Tate for 44. Five wickets then fell for 10 runs and though Nunes made 37, Vallance Jupp and Tich Freeman took the last five wickets in two hours. Following on, the West Indies did worse, and lost their first six wickets for just 44 runs before the end of the second day. On the final morning, 52 from Small and 44 from Browne brought the second innings total to 166 before the match ended after 90 minutes' play.

2nd Test

The tourists made two changes, bringing in Hoad, who had made an unbeaten 149 in a two-day match against Worcestershire three days before this Test started, and Scott. They replaced Fernandes and Small. On a lifeless pitch, West Indies won the toss and batted. Roach, promoted to open, made 50 and put on 48 with Challenor and 46 with Martin. The team then collapsed to 133 for six before Browne and Scott took the total beyond 200. Freeman took five wickets for 54 runs for England. Hobbs, restored to health, and Sutcliffe made 119 for the first England wicket. After three quick wickets, Wally Hammond with 63 put on 120 with Douglas Jardine. A further small collapse followed, including Chapman retiring injured, and Jardine, top scorer with 83, but the tail took the total to 351.
In the second innings, West Indies lost two wickets quickly, but Martin and St Hill put on 55 and at the end of the second day, West Indies were 71 for four. The innings ended rather abjectly on the third morning, only 44 further runs being made in 50 minutes. Freeman again took five wickets, this time for 39 runs, to finish with match figures of 10 for 93.

3rd Test

The third match followed a very similar pattern to the second, with the West Indians making a good start through Roach (53) and Challenor (46), but no other batsman getting beyond the 30s. The total of 238 was, though, the highest of the series for the touring side. Hobbs and Sutcliffe made 155 for the first wicket, and Hobbs and Tyldesley then added 129 before Hobbs was out for 159 with the score at 284. Five further wickets then fell in the next hour for 49 runs, all of them to Griffith, whose innings figures of six for 103 were the best of the series for the West Indies. Tate and Harold Larwood then added 61 in 25 minutes and the last three wickets made 105 runs. West Indies' second innings, as in the previous Tests, began badly, and only a stubborn 41 from Martin enabled the side to reach three figures. As at Lord's and Old Trafford, the match was over before lunch on the third day.

Other matches

Including the three Tests, 30 first-class matches were played on the tour, plus six other games. The West Indians won just seven of these 36 matches (five of the first-class games) and lost 12.

By the end of May, the touring side had won matches against Derbyshire and Cambridge University, plus a minor match against Northumberland, and had drawn all the other games. Defeat by Ireland in a three-day game was followed by a tight victory over Middlesex. But the final first-class game before the First Test saw a sensational defeat by the Minor Counties, who won by 42 runs after being made to follow on.

In the final two months of the season, there were good victories over Northamptonshire and Kent, plus a minor match against Staffordshire, for whom Sydney Barnes, aged 55, took five wickets. But there were heavy defeats by Yorkshire, with the tourists dismissed for 58 all out in the second innings, by Warwickshire, and by Sussex, even though the county rested Maurice Tate, Duleepsinhji and James Langridge. A first-class side representing Wales and including Sydney Barnes also won; Barnes took 12 wickets in this match.

At the end of the tour, the West Indians lost three festival matches at Eastbourne, Folkestone and Scarborough.

Individual performances

The star performer on the tour was Learie Constantine, though he failed in the Test matches. He scored the most first-class runs, with 1,381 at an average of 34.52 runs per innings, took the most first-class wickets, with 107 at 22.95 each, and was the only player to score three centuries in the first-class matches.

Martin, Challenor and Roach also scored more than 1,000 runs in the first-class matches, and Hoad, who came good in the second half of the tour, did so in all matches. Challenor, who had scored 1,556 first-class runs at an average of 51.86 and with six centuries in 1923, made just 1,074 runs at 27.53 runs per innings and failed to score a first-class century on the 1928 tour. Griffith took 103 wickets in all matches.

Verdict and aftermath

The poor performance of the West Indian team led to some unfavourable comment. Wisden for 1929, reporting on the tour as a whole, was damning. "So far from improving upon the form of their predecessors, the team of 1928 fell so much below it that everybody was compelled to realise that the playing of Test matches between England and West Indies was a mistake," it said. "Whatever the future may have in store, the time is certainly not yet when the West Indies can hope to challenge England with a reasonable hope of success."

The official verdict was less condemnatory, and MCC reported of the West Indies side that "much of their cricket was of a high order". MCC agreed to send a side to the West Indies for four Test matches in 1929-30. This tour coincided with a second tour to New Zealand, where the first Test matches against that country were played, and several leading England players opted out of both of the tours. But what was, in effect, an England second eleven had to settle for a shared series, with each side winning one match. This series saw the emergence of George Headley as the first great West Indian batting star, and Wisden's prediction of a long haul towards parity with England proved pessimistic.

References
 Wisden Cricketers Almanack 1929 edition
 Also Wisden 1930 and 1931 for MCC report and for information on 1929-30 England tour to West Indies

External links
CricketArchive
"West Indies Cricketers Good Score" – British Pathé footage of the opening tour match
"Our Cricket Visitors" – British Pathé footage of the West Indians at practice

1928 in English cricket
1928 in West Indian cricket
English cricket seasons in the 20th century
International cricket competitions from 1918–19 to 1945
1928

redpajama_set_name: RedPajamaWikipedia


text:
Q: AWS Javascript SDK: upload object with metadata Tye AWS.S3.ManagedUpload object allows you to upload an object specifying "tags", but I don't see any documentation for specifying meta data on an uploaded object. Is this possible? If so, how?

A: Try with the following:
const params = {
  Metadata: {
    'my-key': 'some-value',
  },
}
s3.upload(params, function(err, data) {
  console.log(err, data);
});

Note that the SDK will add the prefix x-amz-meta-


text:
Polar Bear Plunge 2017
Crabpot Players Return To The Windjammer
Landowners Give To The Community
By Alison Geer for Island Eye News
(Photo by Catherine Main)
Landowners of a unique property on Sullivan's Island recently made a gift of conservation as well as a gift for low-income housing.
They donated a conservation easement on this beachfront tract to the East Cooper Land Trust. Executive Director Catherine Main said, "This project is an unprecedented approach to beach retreat in an area of the island that is threatened by erosion and we are grateful to play a role in its permanent protection."
The property owners removed an existing home to allow most of the quarter-acre parcel to return to its natural state. They placed a conservation easement on the property with East Cooper Land Trust that may serve as a reminder to residents of the importance of maintaining natural buffers in the dynamic environment of barrier islands. This conservation project will provide significant scenic views from a public roadway, provide limited beach access, and proactively address shoreline retreat in an area with very high property values.
The removed house was donated by the owners to Metanoia, a Community Development Corporation that has moved it to the Chicora Place neighborhood in North Charleston. The house will be renovated and made available to the community as part of their mission to provide quality affordable rentals.
East Cooper Land Trust works with willing private landowners who wish to permanently preserve the environmental, cultural or historical value of their land. To learn more about conservation easements or make a donation to the East Cooper Land Trust visit www.eastcooperland.org.


16.00-20.00 p.m.: Longobards and Archers.
18.00-19.00 p.m.: Welcome and Dance Group.
19.00-20.00 p.m.: Reconstruction of a Nuptial Train.
21.00-22.00 p.m.: David Riondino presents: Vincenzo Pirrotta "Aeneas' Flight".
17.00-18.00 p.m.: Welcome and Dance Group.
21.00-22.00 p.m.: David Riondino presents: "Pia Dei Tolomei".
18.00-19.00 p.m.: Battle between factions.
18.00-20.00 p.m.: Duel and Longobard funeral.
21.00-22.00 p.m.: David Riondino presents the metrical romance "EREC et ENID".
* Children under 10 years of age free of charge.
** Discounted for children 11-16 years of age, pensionists, disabled, members of armed forces, special conventions for groups.


At ICU PhotoSoc we are dedicated to helping our members flourish as photographers and enjoy spending time with like-minded people.
We welcome everyone from absolute beginners who want to get the best use out of their cameras, to those who want to hone their skills with the use of our dark room and various tutorials throughout the year. In addition to workshops, you can rent equipment from the club including 35mm film cameras and a studio that can be booked!
Also as a member you can also get involved in the Photo Walks were you can see the sights in London and look to take some amazing photos.
Also if you are looking to have your event or club activities photographed please get in touch through our contact form.


text:
Nobbin es un pueblo situado en el distrito de Pomerania Occidental-Rügen, en el estado federado de Mecklemburgo-Pomerania Occidental (Alemania).

Se encuentra al extremo norte de la isla de Rügen, junto a la costa del mar Báltico.

El pueblo, compuesto por unas pocas casas, se encuentra entre la carretera de Altenkirchen a Arkona y la bahía de Tromper Wiek. Como resultado de su atractiva ubicación entre Cape Arkona y la amplia playa de Schaabe, de más de 10 km de largo, el pueblo está dominado por el turismo.

Referencias

Enlaces externos

Localidades de Pomerania Occidental-Rügen
Localidades de la isla de Rügen


Maj. Gen. I. S. Chaturvedi (Retd) is a former Technical Manager(Land System), IHQ of MOD (Army).
……if Paper Evaluation were achieving its aim of ensuring that only those equipments that meet the technical parameters were invited for field evaluation and if that conclusion was to be true then such equipments should by and large clear field evaluation. However, experience does not suggest that. ……. modify …… existing procedure of Paper Evaluation to achieve the desired results much faster in the context of our acquisition philosophy of SUNDER – SASTA – AUR TIKAU alluded to earlier.


text:
European Launch Vehicles
Zurück Zurück zu Research & Innovation
Swiss Space Policy
Swiss Success Stories in Space
Technological Developments for Space Exploration
Earth Observation
Space Surveillance
European Launch Vehicles selected
Manned Space Flights
Informations for Experts
Public Relations / Education
© ESA
The ESA programme to develop and build European launch vehicles began in 1973. Since then, ESA member states have backed the development of five generations of Ariane rockets. They also provided funding for the construction of a launch pad in Kourou, French Guiana. The 30th anniversary of Ariane's maiden flight was celebrated in December 2009. By then, 193 Ariane flights had already taken place, carrying 318 payloads, including 276 large satellites. While the Ariane 5 launch vehicle currently holds the top position on the global market, in 2010 and 2011, Europe expanded its range of marketable launch vehicles to include two new rockets, the Soyuz in Guiana for medium-sized satellites and the Vega for small-sized satellites.
Ariane 5
Letzte Schritte bei der Integration einer Ariane 5 Trägerrakete. Die Nutzlastverkleidung wurde von RUAG Space AG, die Vorrichtung für die mechanische Bodenunterstützung von APCO Technologies SA entwickelt und hergestellt.
The launch of the Ariane 5
One page backwards
One page forward
Contributions made by Swiss industry
Swiss industrial companies have been involved in the development and construction of European launch vehicles from the very early design stages of Ariane 1. Applying its know-how gained primarily from work on NASA programmes, a Swiss consortium run by Oerlikon Space AG - which became RUAG Space AG in 2009 - focussed on developing an external fairing to protect payloads, especially on the Ariane 1 rocket. Their efforts proved quite successful: in 1974, the Swiss consortium was awarded a contract and has been supplying fairings for every generation of Ariane to the current Ariane 5. The rest belongs to the history of the Swiss space industry: several generations of Swiss-made fairings have flown or still fly on all of the versions of the Ariane rocket as well as on the Atlas V rockets manufactured by Lockheed Martin. The Swiss-designed fairing will also be used for the ESA's future Vega launch vehicles. In addition, Swiss companies have contributed to the construction of test installations, to the development and maintenance of the European launch pad as well as to the equipment used to transport fairing components for payloads and satellites.
ESA Launchers
SERI, Oliver Botta
https://www.sbfi.admin.ch/content/sbfi/en/home/research-and-innovation/space/european-launch-vehicles.html


text:
In past years, the WIAA has held its west-side semifinals at the Tacoma Dome, while semifinals on the east side of the state were held at a handful of approved outdoor facilities. But in January, the executive board decided to move the semifinals away from the Tacoma Dome in favor of regional locations, in part because of the rising costs to use the dome.
The state championship games for all six classifications will be played Dec. 2-3 at the Tacoma Dome.
Which Whatcom football team dropped in the latest rankings despite winning last week?


text:
Privacy Act 2020 – model clauses released but 'safe harbour' regulations not expected until 2022
Authors: Laura Littlewood and Paige Thomas
The Privacy Act 2020 (Privacy Act) is set to come into force on 1 December this year. One of the key changes under the new Act is a new Information Privacy Principle 12 (IPP12), which provides stronger protections for the transfer of personal information to a foreign person or entity.
New regulations will prescribe 'safe harbour' jurisdictions for the purposes of the IPP12. The Ministry of Justice has announced that it is consulting on these regulations and indicated that the initial regulations will not be available until early 2022. Countries will be prioritised for assessment by the Office of the Privacy Commissioner. This will be approached with an annual prioritisation and assessment process, with an anticipated rate of 1-2 countries prescribed annually.
Further information on the Consultation is available here. Consultation is open from 27 October – 4 December 2020.
What does this mean for your business?
For New Zealand businesses, this means you will need to consider other mechanisms in IPP12 to ensure that your practices that involve the transfer of personal information to a foreign person or entity are up to date before the Privacy Act comes into force on 1 December 2020.
The other mechanisms provided under IPP12 apply if you believe, on reasonable grounds, that:
the overseas agency is subject to the Privacy Act,
the overseas agency is subject to privacy laws that, overall, provide comparable safeguards to those in the Privacy Act,
the overseas agency is otherwise required to protect the information in a way that, overall, provides comparable safeguards to those in the Privacy Act e.g. pursuant to an agreement (see model clauses below), or
you have authorisation from the individual concerned after you have informed them that an overseas agency may not be required to protect the information in a way that, overall, provides comparable safeguards to the Privacy Act.
On a practical level, these are a number of steps that you may consider to rely on these alternative mechanisms. For example, developing a company (or industry) position on comparable jurisdictions, updating contracts and templates, and updating privacy policies and statements.
The Office of the Privacy Commissioner has developed model clauses to assist businesses that are seeking to rely on the overseas agency being subject on agreement that includes comparable safeguards to the Privacy Act 2020. The model clauses are intended to assist with compliance - they are not mandatory. Organisations can modify the clauses to suit their needs or use their own form of contract clauses, so long as the key privacy protections are included.
Further information on the model clauses is available here.
Cloud services and other service providers
Guidance from the Office of the Privacy Commissioner has also emphasised that IPP12 does not apply if information is sent to an agent for storage or processing. For example, IPP12 will not apply if you are using offshore cloud providers or other agents to store or process your data, so long as the agent or cloud provider is not using that information for its own purposes.
You will remain responsible for the acts or omissions of those service providers and it is therefore essential that you have robust up-to-date contracts in place, including to address the new framework for the mandatory reporting of privacy breaches.
The Office of the Privacy Commissioner has indicated that it is preparing more detailed guidance about how the Privacy Act applies to information transferred to a cloud provider, software as a service provider or other agent.
Bell Gully Privacy Guide and Chatbot
Bell Gully's privacy team has been closely monitoring guidance on the Privacy Act and developing market practice.
Please see our Guide to the Privacy Act 2020. This includes a practical checklist to ensure that you are prepared.
Bell Gully has also launched a Privacy Breach Chatbot.
Next steps
With one month until the new Privacy Act comes into effect, now is the time to ensure your privacy practices are up-to-date.
If you would like to discuss the updates discussed above, or require any other guidance on the new Privacy Act, please contact our specialist privacy team or your usual Bell Gully adviser.
Laura Littlewood Partner Auckland
Tania Goatley Partner Auckland
Privacy and data protection, Cyber security, Information, communications and technology, Consumer law, Media and communications


text:
import {
  MAGNETIC_MODULE_TYPE,
  TEMPERATURE_MODULE_TYPE,
  THERMOCYCLER_MODULE_TYPE,
  MAGNETIC_MODULE_V1,
  MAGNETIC_MODULE_V2,
} from '@opentrons/shared-data'
import {
  orderedStepIds,
  labwareInvariantProperties,
  moduleInvariantProperties,
  presavedStepForm,
  savedStepForms,
  unsavedForm,
} from '../reducers'
import {
  _getPipetteEntitiesRootState,
  _getLabwareEntitiesRootState,
  _getInitialDeckSetupRootState,
} from '../selectors'
import { handleFormChange } from '../../steplist/formLevel/handleFormChange'
import { moveDeckItem } from '../../labware-ingred/actions'
import {
  INITIAL_DECK_SETUP_STEP_ID,
  SPAN7_8_10_11_SLOT,
  PAUSE_UNTIL_TEMP,
} from '../../constants'
import { PROFILE_CYCLE, PROFILE_STEP } from '../../form-types'
import { PRESAVED_STEP_ID } from '../../steplist/types'
import {
  createPresavedStepForm,
  type CreatePresavedStepFormArgs,
} from '../utils/createPresavedStepForm'
import { createInitialProfileCycle } from '../utils/createInitialProfileItems'
import { getLabwareIsCompatible } from '../../utils/labwareModuleCompatibility'
import { uuid } from '../../utils'
import type { DeckSlot } from '../../types'
jest.mock('../../labware-defs/utils')
jest.mock('../selectors')
jest.mock('../../steplist/formLevel/handleFormChange')
jest.mock('../utils/createPresavedStepForm')
jest.mock('../../utils/labwareModuleCompatibility')
jest.mock('../../utils')

const mockUuid: JestMockFn<[], string> = uuid

const mockCreatePresavedStepForm: JestMockFn<
  [CreatePresavedStepFormArgs],
  any
> = createPresavedStepForm
const handleFormChangeMock: JestMockFn<
  [{ [string]: any }, { [string]: any }, any, any],
  { [string]: any }
> = handleFormChange

const getLabwareIsCompatibleMock: JestMockFn<
  [any],
  boolean
> = getLabwareIsCompatible

const mock_getPipetteEntitiesRootState: JestMockFn<
  [any],
  any
> = _getPipetteEntitiesRootState
const mock_getLabwareEntitiesRootState: JestMockFn<
  [any],
  any
> = _getLabwareEntitiesRootState
const mock_getInitialDeckSetupRootState: JestMockFn<
  [any],
  any
> = _getInitialDeckSetupRootState

afterEach(() => {
  jest.clearAllMocks()
})

describe('orderedStepIds reducer', () => {
  it('should add a saved step when that step is new', () => {
    const state = ['99']
    const action = {
      type: 'SAVE_STEP_FORM',
      payload: { id: '123', stepType: 'moveLiquid' },
    }
    expect(orderedStepIds(state, action)).toEqual(['99', '123'])
  })

  it('should not update when an existing step is saved', () => {
    const state = ['99', '123', '11']
    const action = {
      type: 'SAVE_STEP_FORM',
      payload: { id: '123', stepType: 'moveLiquid' },
    }
    expect(orderedStepIds(state, action)).toBe(state)
  })

  describe('reorder steps', () => {
    const state = ['1', '2', '3', '4']
    const testCases = [
      {
        label: '+1 to first',
        payload: {
          delta: 1,
          stepId: '1',
        },
        expected: ['2', '1', '3', '4'],
      },
      {
        label: '+0 to first: no change',
        payload: {
          delta: 0,
          stepId: '1',
        },
        expected: state,
      },
      {
        label: '-1 to first: no change',
        payload: {
          delta: -1,
          stepId: '1',
        },
        expected: state,
      },
      {
        label: '-10 to first: no change',
        payload: {
          delta: -10,
          stepId: '1',
        },
        expected: state,
      },

      {
        label: '-1 to second',
        payload: {
          delta: -1,
          stepId: '2',
        },
        expected: ['2', '1', '3', '4'],
      },
      {
        label: '-10 to second',
        payload: {
          delta: -10,
          stepId: '2',
        },
        expected: ['2', '1', '3', '4'],
      },

      {
        label: '+1 to last: no change',
        payload: {
          delta: 1,
          stepId: '4',
        },
        expected: state,
      },
      {
        label: '+10 to last: no change',
        payload: {
          delta: 10,
          stepId: '4',
        },
        expected: state,
      },
    ]

    testCases.forEach(({ label, payload, expected }) => {
      it(label, () => {
        const action = {
          type: 'REORDER_SELECTED_STEP',
          payload,
        }
        expect(orderedStepIds(state, action)).toEqual(expected)
      })
    })
  })
})

describe('labwareInvariantProperties reducer', () => {
  it('replace custom labware def', () => {
    const prevState = {
      labwareIdA1: { labwareDefURI: 'foo/a/1' },
      labwareIdA2: { labwareDefURI: 'foo/a/1' },
      labwareIdB: { labwareDefURI: 'foo/b/1' },
    }
    const result = labwareInvariantProperties(prevState, {
      type: 'REPLACE_CUSTOM_LABWARE_DEF',
      payload: {
        defURIToOverwrite: 'foo/a/1',
        newDef: { parameters: { loadName: 'a' }, version: 2, namespace: 'foo' },
        isOverwriteMismatched: false,
      },
    })
    expect(result).toEqual({
      // changed
      labwareIdA1: { labwareDefURI: 'foo/a/2' },
      labwareIdA2: { labwareDefURI: 'foo/a/2' },
      // unchanged
      labwareIdB: { labwareDefURI: 'foo/b/1' },
    })
  })
})

describe('moduleInvariantProperties reducer', () => {
  let prevState
  const existingModuleId = 'existingModuleId'
  const newId = 'newModuleId'
  beforeEach(() => {
    prevState = {
      [existingModuleId]: {
        id: existingModuleId,
        slot: '1',
        type: MAGNETIC_MODULE_TYPE,
        model: 'someMagModel',
      },
    }
  })

  it('create module', () => {
    const newModuleData = {
      id: newId,
      slot: '3',
      type: TEMPERATURE_MODULE_TYPE,
      model: 'someTempModel',
    }
    const result = moduleInvariantProperties(prevState, {
      type: 'CREATE_MODULE',
      payload: newModuleData,
    })
    expect(result).toEqual({
      ...prevState,
      [newId]: {
        id: newId,
        type: newModuleData.type,
        model: newModuleData.model,
      },
    })
  })

  it('edit module (change its model)', () => {
    const newModel = 'someDifferentModel'
    const result = moduleInvariantProperties(prevState, {
      type: 'EDIT_MODULE',
      payload: { id: existingModuleId, model: newModel },
    })
    expect(result).toEqual({
      [existingModuleId]: { ...prevState.existingModuleId, model: newModel },
    })
  })

  it('delete module', () => {
    const result = moduleInvariantProperties(prevState, {
      type: 'DELETE_MODULE',
      payload: { id: existingModuleId },
    })
    expect(result).toEqual({})
  })
})

type MakeDeckSetupStepArgs = {
  labwareLocationUpdate?: { [id: string]: DeckSlot },
  pipetteLocationUpdate?: { [id: string]: DeckSlot },
  moduleLocationUpdate?: { [id: string]: DeckSlot },
}

const makeDeckSetupStep = (args: MakeDeckSetupStepArgs = {}) => ({
  stepType: 'manualIntervention',
  id: '__INITIAL_DECK_SETUP_STEP__',
  labwareLocationUpdate: args.labwareLocationUpdate || {},
  pipetteLocationUpdate: args.pipetteLocationUpdate || {},
  moduleLocationUpdate: args.moduleLocationUpdate || {},
})

const makePrevRootState = (args: MakeDeckSetupStepArgs): any => ({
  savedStepForms: {
    [INITIAL_DECK_SETUP_STEP_ID]: makeDeckSetupStep(args),
  },
})

describe('savedStepForms reducer: initial deck setup step', () => {
  const existingLabwareId = '_existingLabwareId'
  const otherLabwareId = '_otherLabwareId'
  const newLabwareId = '_newLabwareId'
  const moduleId = '_moduleId'
  const otherModuleId = '_otherModuleId'
  const labwareOnModuleId = '_labwareOnModuleId'

  describe('create (or duplicate) new labware', () => {
    const newSlot = '8'
    const testCases = [
      {
        testName: 'duplicate labware',
        action: {
          type: 'DUPLICATE_LABWARE',
          payload: {
            templateLabwareId: existingLabwareId,
            duplicateLabwareId: newLabwareId,
            duplicateLabwareNickname: 'new labware nickname',
            slot: newSlot,
          },
        },
      },
      {
        testName: `create labware in slot ${newSlot}`,
        action: {
          type: 'CREATE_CONTAINER',
          payload: {
            slot: newSlot,
            labwareDefURI: 'fixtures/foo/1',
            id: newLabwareId,
          },
        },
      },
    ]

    testCases.forEach(({ testName, action }) => {
      it(testName, () => {
        const prevRootState = makePrevRootState({
          labwareLocationUpdate: {
            [existingLabwareId]: '1',
          },
        })
        const result = savedStepForms(prevRootState, action)
        expect(
          result[INITIAL_DECK_SETUP_STEP_ID].labwareLocationUpdate
        ).toEqual({
          [existingLabwareId]: '1',
          [newLabwareId]: newSlot,
        })
      })
    })
  })

  describe('move deck item', () => {
    const testCases: Array<{|
      testName: string,
      sourceSlot: DeckSlot,
      destSlot: DeckSlot,
      makeStateArgs: MakeDeckSetupStepArgs,
      expectedLabwareLocations?: Object,
      expectedModuleLocations?: Object,
    |}> = [
      {
        testName: 'move labware to empty slot -> simple move',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: {
            [existingLabwareId]: '1',
          },
        },
        expectedLabwareLocations: {
          [existingLabwareId]: '3',
        },
      },
      {
        testName: 'move labware to slot with labware -> swap labware',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: {
            [existingLabwareId]: '1',
            [otherLabwareId]: '3',
          },
        },
        expectedLabwareLocations: {
          [existingLabwareId]: '3',
          [otherLabwareId]: '1',
        },
      },
      {
        testName: 'move labware empty module -> labware added to module',
        sourceSlot: '1',
        destSlot: moduleId,
        makeStateArgs: {
          labwareLocationUpdate: {
            [existingLabwareId]: '1',
          },
          moduleLocationUpdate: {
            [moduleId]: '3',
          },
        },
        expectedLabwareLocations: {
          [existingLabwareId]: moduleId,
        },
        expectedModuleLocations: {
          [moduleId]: '3',
        },
      },
      {
        // NOTE: if labware is incompatible, it's up to the UI to block this.
        testName:
          'move labware to slot with occupied module -> swap labware, module stays',
        sourceSlot: '1',
        destSlot: moduleId,
        makeStateArgs: {
          labwareLocationUpdate: {
            [existingLabwareId]: '1',
            [labwareOnModuleId]: moduleId,
          },
          moduleLocationUpdate: {
            [moduleId]: '3',
          },
        },
        expectedLabwareLocations: {
          [existingLabwareId]: moduleId,
          [labwareOnModuleId]: '1',
        },
        expectedModuleLocations: {
          [moduleId]: '3',
        },
      },
      {
        testName: 'move empty module to empty slot -> simple move',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: {},
          moduleLocationUpdate: {
            [moduleId]: '1',
          },
        },
        expectedLabwareLocations: {},
        expectedModuleLocations: { [moduleId]: '3' },
      },
      {
        testName:
          'move empty module to slot with incompatible labware -> swap slots, do not add labware to module',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: { [existingLabwareId]: '3' },
          moduleLocationUpdate: { [moduleId]: '1' },
        },
        deckSetup: {
          labware: {
            [existingLabwareId]: {
              id: existingLabwareId,
              slot: '3',
              def: { foo: 'fake def' },
            },
          },
          pipettes: {},
          modules: {
            [moduleId]: {
              id: moduleId,
              type: MAGNETIC_MODULE_TYPE,
              model: MAGNETIC_MODULE_V2,
              slot: '1',
            },
          },
        },
        labwareIsCompatible: false,
        expectedLabwareLocations: { [existingLabwareId]: '1' },
        expectedModuleLocations: { [moduleId]: '3' },
      },
      {
        testName:
          'move empty module to slot with compatible labware -> put module under labware',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: { [existingLabwareId]: '3' },
          moduleLocationUpdate: { [moduleId]: '1' },
        },
        deckSetup: {
          labware: {
            [existingLabwareId]: {
              id: existingLabwareId,
              slot: '3',
              def: { foo: 'fake def' },
            },
          },
          pipettes: {},
          modules: {
            [moduleId]: {
              id: moduleId,
              type: MAGNETIC_MODULE_TYPE,
              model: MAGNETIC_MODULE_V2,
              slot: '1',
            },
          },
        },
        labwareIsCompatible: true,
        expectedLabwareLocations: { [existingLabwareId]: moduleId },
        expectedModuleLocations: { [moduleId]: '3' },
      },
      {
        testName:
          'move occupied module to slot with labware -> swap slots, do not change labware on module (even if compatible)',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: {
            [existingLabwareId]: '3',
            [labwareOnModuleId]: moduleId,
          },
          moduleLocationUpdate: { [moduleId]: '1' },
        },
        deckSetup: {
          labware: {
            [existingLabwareId]: {
              id: existingLabwareId,
              slot: '3',
              def: { foo: 'fake def' },
            },
            [labwareOnModuleId]: {
              id: labwareOnModuleId,
              slot: moduleId,
              def: { foo: 'fake def' },
            },
          },
          pipettes: {},
          modules: {
            [moduleId]: {
              id: moduleId,
              type: MAGNETIC_MODULE_TYPE,
              model: MAGNETIC_MODULE_V2,
              slot: '1',
            },
          },
        },
        labwareIsCompatible: true,
        expectedLabwareLocations: {
          [existingLabwareId]: '1',
          [labwareOnModuleId]: moduleId,
        },
        expectedModuleLocations: { [moduleId]: '3' },
      },
      {
        testName: 'move labware off of module to empty slot',
        sourceSlot: moduleId,
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: {
            [labwareOnModuleId]: moduleId,
          },
          moduleLocationUpdate: { [moduleId]: '1' },
        },
        expectedLabwareLocations: { [labwareOnModuleId]: '3' },
        expectedModuleLocations: { [moduleId]: '1' },
      },
      {
        testName: 'move empty module to occupied module -> swap, keep pairings',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: {
            [labwareOnModuleId]: moduleId,
          },
          moduleLocationUpdate: {
            [moduleId]: '1',
            [otherModuleId]: '3',
          },
        },
        expectedLabwareLocations: {
          [labwareOnModuleId]: moduleId,
        },
        expectedModuleLocations: {
          [moduleId]: '3',
          [otherModuleId]: '1',
        },
      },
      {
        testName: 'empty module to empty module -> swap',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: {},
          moduleLocationUpdate: {
            [moduleId]: '1',
            [otherModuleId]: '3',
          },
        },
        expectedLabwareLocations: {},
        expectedModuleLocations: {
          [moduleId]: '3',
          [otherModuleId]: '1',
        },
      },
      {
        testName: 'occupied module to occupied module -> swap, keep pairings',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: {
            [labwareOnModuleId]: moduleId,
            [otherLabwareId]: otherModuleId,
          },
          moduleLocationUpdate: {
            [moduleId]: '1',
            [otherModuleId]: '3',
          },
        },
        expectedLabwareLocations: {
          [labwareOnModuleId]: moduleId,
          [otherLabwareId]: otherModuleId,
        },
        expectedModuleLocations: {
          [moduleId]: '3',
          [otherModuleId]: '1',
        },
      },
      {
        testName: 'occupied module to empty module -> swap, keep pairings',
        sourceSlot: '1',
        destSlot: '3',
        makeStateArgs: {
          labwareLocationUpdate: {
            [labwareOnModuleId]: moduleId,
          },
          moduleLocationUpdate: {
            [moduleId]: '1',
            [otherModuleId]: '3',
          },
        },
        expectedLabwareLocations: {
          [labwareOnModuleId]: moduleId,
        },
        expectedModuleLocations: {
          [moduleId]: '3',
          [otherModuleId]: '1',
        },
      },
    ]
    testCases.forEach(
      ({
        testName,
        sourceSlot,
        destSlot,
        makeStateArgs,
        deckSetup,
        labwareIsCompatible,
        expectedLabwareLocations,
        expectedModuleLocations,
      }) => {
        it(testName, () => {
          mock_getInitialDeckSetupRootState.mockReturnValue(deckSetup)
          getLabwareIsCompatibleMock.mockReturnValue(labwareIsCompatible)
          const prevRootState = makePrevRootState(makeStateArgs)
          const action = moveDeckItem(sourceSlot, destSlot)
          const result = savedStepForms(prevRootState, action)

          if (expectedLabwareLocations) {
            expect(
              result[INITIAL_DECK_SETUP_STEP_ID].labwareLocationUpdate
            ).toEqual(expectedLabwareLocations)
          }
          if (expectedModuleLocations) {
            expect(
              result[INITIAL_DECK_SETUP_STEP_ID].moduleLocationUpdate
            ).toEqual(expectedModuleLocations)
          }
        })
      }
    )
  })

  it('delete labware -> removes labware from initial deck setup step', () => {
    const labwareToDeleteId = '__labwareToDelete'
    const prevRootState = makePrevRootState({
      labwareLocationUpdate: {
        [existingLabwareId]: '1',
        [labwareToDeleteId]: '2',
      },
    })
    const action = {
      type: 'DELETE_CONTAINER',
      payload: { labwareId: labwareToDeleteId },
    }
    const result = savedStepForms(prevRootState, action)
    expect(result[INITIAL_DECK_SETUP_STEP_ID].labwareLocationUpdate).toEqual({
      [existingLabwareId]: '1',
    })
  })

  it('delete pipettes -> removes pipette(s) from initial deck setup step', () => {
    const leftPipetteId = '__leftPipette'
    const rightPipetteId = '__rightPipette'
    const prevRootState = makePrevRootState({
      pipetteLocationUpdate: {
        [leftPipetteId]: 'left',
        [rightPipetteId]: 'right',
      },
    })
    const testCases = [
      {
        pipettesToDelete: [leftPipetteId],
        expected: { [rightPipetteId]: 'right' },
      },
      { pipettesToDelete: [leftPipetteId, rightPipetteId], expected: {} },
    ]
    testCases.forEach(({ pipettesToDelete, expected }) => {
      const action = {
        type: 'DELETE_PIPETTES',
        payload: pipettesToDelete,
      }
      const result = savedStepForms(prevRootState, action)
      expect(result[INITIAL_DECK_SETUP_STEP_ID].pipetteLocationUpdate).toEqual(
        expected
      )
    })
  })

  describe('create module', () => {
    describe('NO existing steps', () => {
      const destSlot = '3'
      const testCases = [
        {
          testName:
            'create module in empty deck slot (labware in unrelated slot unaffected)',
          makeStateArgs: {
            labwareLocationUpdate: { [existingLabwareId]: '6' },
          },
          expectedLabwareLocations: { [existingLabwareId]: '6' },
          expectedModuleLocations: { [moduleId]: destSlot },
        },
        {
          testName:
            'create module in deck slot occupied with labware -> move that labware to the new module',
          makeStateArgs: {
            labwareLocationUpdate: { [existingLabwareId]: destSlot },
          },
          expectedLabwareLocations: { [existingLabwareId]: moduleId },
          expectedModuleLocations: { [moduleId]: destSlot },
        },
      ]
      testCases.forEach(
        ({
          testName,
          makeStateArgs,
          expectedLabwareLocations,
          expectedModuleLocations,
        }) => {
          it(testName, () => {
            const action = {
              type: 'CREATE_MODULE',
              payload: {
                id: moduleId,
                slot: destSlot,
                type: TEMPERATURE_MODULE_TYPE,
                model: 'someTempModel',
              },
            }
            const prevRootState = makePrevRootState(makeStateArgs)
            const result = savedStepForms(prevRootState, action)
            expect(
              result[INITIAL_DECK_SETUP_STEP_ID].labwareLocationUpdate
            ).toEqual(expectedLabwareLocations)
            expect(
              result[INITIAL_DECK_SETUP_STEP_ID].moduleLocationUpdate
            ).toEqual(expectedModuleLocations)
          })
        }
      )
    })
    describe('existing steps', () => {
      let prevRootStateWithMagAndTCSteps
      beforeEach(() => {
        prevRootStateWithMagAndTCSteps = {
          savedStepForms: {
            ...makePrevRootState().savedStepForms,
            ...{
              mag_step_form_id: {
                stepType: 'magnet',
                moduleId: 'magdeckId',
              },
            },
            ...{
              TC_step_form_id: {
                stepType: 'thermocycler',
                moduleId: 'TCId',
              },
            },
          },
        }
      })
      const magneticStepCases = [
        {
          testName: 'create mag mod -> override mag step module id',
          action: {
            type: 'CREATE_MODULE',
            payload: {
              id: 'newMagdeckId',
              slot: '1',
              type: MAGNETIC_MODULE_TYPE,
              model: 'someMagModel',
            },
          },
          expectedModuleId: 'newMagdeckId',
        },
        {
          testName: 'create temp mod -> DO NOT override mag step module id',
          action: {
            type: 'CREATE_MODULE',
            payload: {
              id: 'tempdeckId',
              slot: '1',
              type: TEMPERATURE_MODULE_TYPE,
              model: 'someTempModel',
            },
          },
          expectedModuleId: 'magdeckId',
        },
        {
          testName: 'create TC -> DO NOT override mag step module id',
          action: {
            type: 'CREATE_MODULE',
            payload: {
              id: 'ThermocyclerId',
              slot: '1',
              type: THERMOCYCLER_MODULE_TYPE,
              model: 'someThermoModel',
            },
          },
          expectedModuleId: 'magdeckId',
        },
      ]

      const TCStepCases = [
        {
          testName: 'create TC -> override TC step module id',
          action: {
            type: 'CREATE_MODULE',
            payload: {
              id: 'NewTCId',
              slot: SPAN7_8_10_11_SLOT,
              type: THERMOCYCLER_MODULE_TYPE,
              model: 'someTCModel',
            },
          },
          expectedModuleId: 'NewTCId',
        },
        {
          testName: 'create temp mod -> DO NOT override TC step module id',
          action: {
            type: 'CREATE_MODULE',
            payload: {
              id: 'tempdeckId',
              slot: '1',
              type: TEMPERATURE_MODULE_TYPE,
              model: 'someTempModel',
            },
          },
          expectedModuleId: 'TCId',
        },
        {
          testName: 'create magnetic mod -> DO NOT override TC step module id',
          action: {
            type: 'CREATE_MODULE',
            payload: {
              id: 'newMagdeckId',
              slot: '1',
              type: MAGNETIC_MODULE_TYPE,
              model: 'someMagModel',
            },
          },
          expectedModuleId: 'TCId',
        },
      ]

      magneticStepCases.forEach(({ testName, action, expectedModuleId }) => {
        it(testName, () => {
          const result = savedStepForms(prevRootStateWithMagAndTCSteps, action)
          if (action.payload.type)
            expect(result.mag_step_form_id.moduleId).toBe(expectedModuleId)
        })
      })

      TCStepCases.forEach(({ testName, action, expectedModuleId }) => {
        it(testName, () => {
          const result = savedStepForms(prevRootStateWithMagAndTCSteps, action)
          if (action.payload.type)
            expect(result.TC_step_form_id.moduleId).toBe(expectedModuleId)
        })
      })
    })
  })

  describe('delete module -> removes module from initial deck setup step', () => {
    const testCases = [
      {
        testName: 'delete unoccupied module',
        makeStateArgs: {
          moduleLocationUpdate: {
            [moduleId]: '1',
            [otherModuleId]: '2',
          },
        },
        expectedLabwareLocations: {},
        expectedModuleLocations: {
          [otherModuleId]: '2',
        },
      },
      {
        testName: 'delete occupied module -> labware goes into its slot',
        makeStateArgs: {
          labwareLocationUpdate: { [labwareOnModuleId]: moduleId },
          moduleLocationUpdate: {
            [moduleId]: '3',
          },
        },
        expectedLabwareLocations: { [labwareOnModuleId]: '3' },
        expectedModuleLocations: {},
      },
      {
        testName:
          'delete occupied module in span7_8_10_11 slot -> labware goes into slot 7',
        makeStateArgs: {
          labwareLocationUpdate: { [labwareOnModuleId]: moduleId },
          moduleLocationUpdate: {
            [moduleId]: SPAN7_8_10_11_SLOT,
          },
        },
        expectedLabwareLocations: { [labwareOnModuleId]: '7' },
        expectedModuleLocations: {},
      },
    ]
    testCases.forEach(
      ({
        testName,
        makeStateArgs,
        expectedLabwareLocations,
        expectedModuleLocations,
      }) => {
        it(testName, () => {
          const action = { type: 'DELETE_MODULE', payload: { id: moduleId } }
          const prevRootState = makePrevRootState(makeStateArgs)
          const result = savedStepForms(prevRootState, action)
          expect(
            result[INITIAL_DECK_SETUP_STEP_ID].moduleLocationUpdate
          ).toEqual(expectedModuleLocations)
          expect(
            result[INITIAL_DECK_SETUP_STEP_ID].labwareLocationUpdate
          ).toEqual(expectedLabwareLocations)
        })
      }
    )
  })

  describe('delete module -> removes references to module from step forms', () => {
    const stepId = '_stepId'
    const action = { type: 'DELETE_MODULE', payload: { id: moduleId } }
    const getPrevRootStateWithStep = step => ({
      savedStepForms: {
        [INITIAL_DECK_SETUP_STEP_ID]: makeDeckSetupStep({
          moduleLocationUpdate: {
            [moduleId]: '1',
            [otherModuleId]: '2',
          },
        }),
        [stepId]: step,
      },
    })

    const testCases = [
      {
        testName: 'pause -> wait until temperature step',
        step: {
          id: stepId,
          stepType: 'pause',
          stepName: 'pause until 4C',
          stepDetails: 'some details',
          pauseAction: PAUSE_UNTIL_TEMP,
          pauseHour: null,
          pauseMinute: null,
          pauseSecond: null,
          pauseMessage: '',
          moduleId,
          pauseTemperature: '4',
        },
      },
      {
        testName: 'set temperature step',
        step: {
          id: stepId,
          stepType: 'temperature',
          stepName: 'temperature to 4',
          stepDetails: 'some details',
          moduleId,
          setTemperature: 'true',
          targetTemperature: '4',
        },
      },
      {
        testName: 'magnet step',
        step: {
          id: stepId,
          stepType: 'magnet',
          stepName: 'engage magnet',
          stepDetails: 'some details',
          moduleId,
          magnetAction: 'engage',
          engageHeight: '4',
        },
      },
    ]

    testCases.forEach(({ testName, step }) => {
      it(testName, () => {
        const result = savedStepForms(getPrevRootStateWithStep(step), action)
        expect(result[stepId]).toEqual({ ...step, moduleId: null })
      })
    })
  })

  describe('EDIT_MODULE', () => {
    it('should set engageHeight to null for all Magnet > Engage steps when a magnet module has its model changed, unless height matches default', () => {
      mock_getInitialDeckSetupRootState.mockReturnValue({
        labware: {
          magPlateId: {
            id: 'magPlateId',
            slot: 'magModuleId',
            def: { parameters: { magneticModuleEngageHeight: 12 } },
          },
        },
        pipettes: {},
        modules: {
          magModuleId: {
            id: 'magModuleId',
            type: MAGNETIC_MODULE_TYPE,
            model: MAGNETIC_MODULE_V1,
            slot: '1',
          },
        },
      })

      const action = {
        type: 'EDIT_MODULE',
        payload: { id: 'magModuleId', model: 'magneticModuleV2' },
      }

      const prevRootState = {
        savedStepForms: {
          magnetEngageStepId: {
            stepType: 'magnet',
            magnetAction: 'engage',
            moduleId: 'magModuleId',
            engageHeight: '24', // = 12 * 2 b/c we're going V1 -> V2
          },
          magnetDisengageStepId: {
            stepType: 'magnet',
            magnetAction: 'disengage',
            engageHeight: null,
            moduleId: 'magModuleId',
          },
          nonDefaultMagnetEngageStepId: {
            stepType: 'magnet',
            magnetAction: 'engage',
            moduleId: 'magModuleId',
            engageHeight: '8',
          },
          unrelatedMagnetEngageStepId: {
            stepType: 'magnet',
            magnetAction: 'engage',
            moduleId: 'otherMagModuleId', // not 'magModuleId'
            engageHeight: '8',
          },
        },
      }
      const result = savedStepForms(prevRootState, action)
      expect(result).toEqual({
        magnetEngageStepId: {
          stepType: 'magnet',
          magnetAction: 'engage',
          engageHeight: '12', // V2 units default
          moduleId: 'magModuleId',
        },
        magnetDisengageStepId:
          prevRootState.savedStepForms.magnetDisengageStepId,
        nonDefaultMagnetEngageStepId: {
          stepType: 'magnet',
          magnetAction: 'engage',
          moduleId: 'magModuleId',
          engageHeight: null,
        },
        // module id not matching, unchanged
        unrelatedMagnetEngageStepId:
          prevRootState.savedStepForms.unrelatedMagnetEngageStepId,
      })
    })
  })
})

describe('unsavedForm reducer', () => {
  const someState: any = { something: 'foo' }

  it('should take on the payload of the POPULATE_FORM action', () => {
    const payload = { formStuff: 'example' }
    const result = unsavedForm(someState, { type: 'POPULATE_FORM', payload })
    expect(result).toEqual(payload)
  })

  it('should use handleFormChange to update the state with CHANGE_FORM_INPUT action', () => {
    const rootState: any = {
      unsavedForm: { existingField: 123 },
    }
    const action = {
      type: 'CHANGE_FORM_INPUT',
      payload: { update: { someField: -1 } },
    }

    handleFormChangeMock.mockReturnValue({ someField: 42 })
    mock_getPipetteEntitiesRootState.mockReturnValue(
      'pipetteEntitiesPlaceholder'
    )
    mock_getLabwareEntitiesRootState.mockReturnValue(
      'labwareEntitiesPlaceholder'
    )

    const result = unsavedForm(rootState, action)
    expect(mock_getPipetteEntitiesRootState.mock.calls).toEqual([[rootState]])
    expect(mock_getLabwareEntitiesRootState.mock.calls).toEqual([[rootState]])
    expect(handleFormChangeMock.mock.calls).toEqual([
      [
        action.payload.update,
        rootState.unsavedForm,
        'pipetteEntitiesPlaceholder',
        'labwareEntitiesPlaceholder',
      ],
    ])
    expect(result).toEqual({ existingField: 123, someField: 42 })
  })

  it("should switch out pipettes via handleFormChange in response to SUBSTITUTE_STEP_FORM_PIPETTES if the unsaved form's ID is in range", () => {
    const action = {
      type: 'SUBSTITUTE_STEP_FORM_PIPETTES',
      payload: {
        substitutionMap: { oldPipetteId: 'newPipetteId' },
        startStepId: '3',
        endStepId: '5',
      },
    }
    const rootState = {
      orderedStepIds: ['1', '3', '4', '5', '6'],
      unsavedForm: { pipette: 'oldPipetteId', id: '4', otherField: 'blah' },
    }

    handleFormChangeMock.mockReturnValue({ pipette: 'newPipetteId' })
    mock_getPipetteEntitiesRootState.mockReturnValue(
      'pipetteEntitiesPlaceholder'
    )
    mock_getLabwareEntitiesRootState.mockReturnValue(
      'labwareEntitiesPlaceholder'
    )

    const result = unsavedForm(rootState, action)
    expect(mock_getPipetteEntitiesRootState.mock.calls).toEqual([[rootState]])
    expect(mock_getLabwareEntitiesRootState.mock.calls).toEqual([[rootState]])
    expect(handleFormChangeMock.mock.calls).toEqual([
      [
        { pipette: 'newPipetteId' },
        rootState.unsavedForm,
        'pipetteEntitiesPlaceholder',
        'labwareEntitiesPlaceholder',
      ],
    ])
    expect(result).toEqual({
      id: '4',
      pipette: 'newPipetteId',
      otherField: 'blah',
    })
  })

  const actionTypes = [
    'CANCEL_STEP_FORM',
    'CREATE_MODULE',
    'DELETE_MODULE',
    'DELETE_STEP',
    'EDIT_MODULE',
    'SAVE_STEP_FORM',
    'SELECT_TERMINAL_ITEM',
  ]
  actionTypes.forEach(actionType => {
    it(`should clear the unsaved form when any ${actionType} action is dispatched`, () => {
      const result = unsavedForm(someState, { type: actionType })
      expect(result).toEqual(null)
    })
  })

  it('should return the result createPresavedStepForm util upon ADD_STEP action', () => {
    mockCreatePresavedStepForm.mockReturnValue(
      'createPresavedStepFormMockResult'
    )
    mock_getInitialDeckSetupRootState.mockReturnValue('initalDeckSetupValue')
    const stateMock = {
      savedStepForms: 'savedStepFormsValue',
      orderedStepIds: 'orderedStepIdsValue',
    }
    const result = unsavedForm(stateMock, {
      type: 'ADD_STEP',
      payload: { id: 'stepId123', stepType: 'moveLiquid' },
      meta: { robotStateTimeline: 'robotStateTimelineValue' },
    })
    expect(result).toEqual('createPresavedStepFormMockResult')
    expect(mockCreatePresavedStepForm.mock.calls).toEqual([
      [
        {
          stepId: 'stepId123',
          stepType: 'moveLiquid',
          pipetteEntities: 'pipetteEntitiesPlaceholder',
          labwareEntities: 'labwareEntitiesPlaceholder',
          savedStepForms: 'savedStepFormsValue',
          orderedStepIds: 'orderedStepIdsValue',
          initialDeckSetup: 'initalDeckSetupValue',
          robotStateTimeline: 'robotStateTimelineValue',
        },
      ],
    ])
  })

  it('should add a profile cycle item upon ADD_PROFILE_CYCLE action', () => {
    const action = { type: 'ADD_PROFILE_CYCLE', payload: null }

    const id = 'newCycleId'
    const profileStepId = 'newProfileStepId'
    // NOTE: because we're using uuid() to create multiple different ids,
    // this test is sensitive to the order that uuid is called in and
    // assumes it's first for cycle id, then next for profile step id
    mockUuid.mockReturnValueOnce(id)
    mockUuid.mockReturnValueOnce(profileStepId)

    const state = {
      unsavedForm: {
        stepType: 'thermocycler',
        orderedProfileItems: [],
        profileItemsById: {},
      },
    }
    const result = unsavedForm(state, action)

    expect(result).toEqual({
      stepType: 'thermocycler',
      orderedProfileItems: [id],
      profileItemsById: {
        [id]: createInitialProfileCycle(id, profileStepId),
      },
    })
  })

  it('should add a profile step item to the specified cycle upon ADD_PROFILE_STEP action with cycleId payload', () => {
    const cycleId = 'someCycleId'
    const stepId = 'newStepId'
    const action = { type: 'ADD_PROFILE_STEP', payload: { cycleId } }

    mockUuid.mockReturnValue(stepId)

    const state = {
      unsavedForm: {
        stepType: 'thermocycler',
        orderedProfileItems: [cycleId],
        profileItemsById: {
          [cycleId]: {
            type: PROFILE_CYCLE,
            id: cycleId,
            repetitions: '1',
            steps: [],
          },
        },
      },
    }
    const result = unsavedForm(state, action)

    expect(result).toEqual({
      stepType: 'thermocycler',
      orderedProfileItems: [cycleId],
      profileItemsById: {
        [cycleId]: {
          ...state.unsavedForm.profileItemsById[cycleId],
          steps: [
            {
              id: stepId,
              type: PROFILE_STEP,
              title: '',
              temperature: '',
              durationMinutes: '',
              durationSeconds: '',
            },
          ],
        },
      },
    })
  })

  it('should remove a profile step item on DELETE_PROFILE_STEP', () => {
    const id = 'stepItemId'
    const action = { type: 'DELETE_PROFILE_STEP', payload: { id } }
    const state = {
      unsavedForm: {
        stepType: 'thermocycler',
        orderedProfileItems: [id],
        profileItemsById: {
          [id]: {
            type: PROFILE_STEP,
            id,
            title: '',
            temperature: '',
            durationMinutes: '',
            durationSeconds: '',
          },
        },
      },
    }
    const result = unsavedForm(state, action)

    expect(result).toEqual({
      stepType: 'thermocycler',
      orderedProfileItems: [],
      profileItemsById: {},
    })
  })

  it('should remove a step item inside a cycle on DELETE_PROFILE_STEP', () => {
    const stepId = 'stepItemId'
    const cycleId = 'cycleId'
    const action = { type: 'DELETE_PROFILE_STEP', payload: { id: stepId } }
    const state = {
      unsavedForm: {
        stepType: 'thermocycler',
        orderedProfileItems: [cycleId],
        profileItemsById: {
          [cycleId]: {
            type: PROFILE_CYCLE,
            id: cycleId,
            steps: [
              {
                type: PROFILE_STEP,
                id: stepId,
                title: '',
                temperature: '',
                durationMinutes: '',
                durationSeconds: '',
              },
            ],
            repetitions: '1',
          },
        },
      },
    }
    const result = unsavedForm(state, action)

    expect(result).toEqual({
      stepType: 'thermocycler',
      orderedProfileItems: [cycleId],
      profileItemsById: {
        [cycleId]: {
          type: PROFILE_CYCLE,
          id: cycleId,
          steps: [],
          repetitions: '1',
        },
      },
    })
  })

  it('should do nothing on DELETE_PROFILE_STEP when the id is a cycle', () => {
    const id = 'cycleItemId'
    const action = { type: 'DELETE_PROFILE_STEP', payload: { id } }
    const state = {
      unsavedForm: {
        stepType: 'thermocycler',
        orderedProfileItems: [id],
        profileItemsById: {
          [id]: {
            type: PROFILE_CYCLE,
            id,
            steps: [],
            repetitions: '1',
          },
        },
      },
    }
    const result = unsavedForm(state, action)
    expect(result).toEqual(state.unsavedForm)
  })

  it('should delete cycle on DELETE_PROFILE_CYCLE', () => {
    const id = 'cycleItemId'
    const action = { type: 'DELETE_PROFILE_CYCLE', payload: { id } }
    const state = {
      unsavedForm: {
        stepType: 'thermocycler',
        orderedProfileItems: [id],
        profileItemsById: {
          [id]: {
            type: PROFILE_CYCLE,
            id,
            steps: [],
            repetitions: '1',
          },
        },
      },
    }
    const result = unsavedForm(state, action)
    expect(result).toEqual({
      stepType: 'thermocycler',
      orderedProfileItems: [],
      profileItemsById: {},
    })
  })

  it('should edit a profile step on the top level with EDIT_PROFILE_STEP', () => {
    const stepId = 'profileStepId'
    const action = {
      type: 'EDIT_PROFILE_STEP',
      payload: { id: stepId, fields: { title: 'x' } },
    }
    const state = {
      unsavedForm: {
        stepType: 'thermocycler',
        orderedProfileItems: [stepId],
        profileItemsById: {
          [stepId]: {
            type: PROFILE_STEP,
            id: stepId,
            title: '',
            temperature: '',
            durationMinutes: '',
            durationSeconds: '',
          },
        },
      },
    }
    const result = unsavedForm(state, action)
    expect(result).toEqual({
      stepType: 'thermocycler',
      orderedProfileItems: [stepId],
      profileItemsById: {
        [stepId]: {
          type: PROFILE_STEP,
          id: stepId,
          title: 'x',
          temperature: '',
          durationMinutes: '',
          durationSeconds: '',
        },
      },
    })
  })

  it('should edit a profile step that is inside a cycle with EDIT_PROFILE_STEP', () => {
    const cycleId = 'cycleId'
    const stepId = 'profileStepId'
    const action = {
      type: 'EDIT_PROFILE_STEP',
      payload: { id: stepId, fields: { title: 'x' } },
    }
    const state = {
      unsavedForm: {
        stepType: 'thermocycler',
        orderedProfileItems: [cycleId],
        profileItemsById: {
          [cycleId]: {
            type: PROFILE_CYCLE,
            id: cycleId,
            steps: [
              {
                type: PROFILE_STEP,
                id: stepId,
                title: '',
                temperature: '',
                durationMinutes: '',
                durationSeconds: '',
              },
            ],
            repetitions: '1',
          },
        },
      },
    }
    const result = unsavedForm(state, action)
    expect(result).toEqual({
      stepType: 'thermocycler',
      orderedProfileItems: [cycleId],
      profileItemsById: {
        [cycleId]: {
          type: PROFILE_CYCLE,
          id: cycleId,
          steps: [
            {
              type: PROFILE_STEP,
              id: stepId,
              title: 'x',
              temperature: '',
              durationMinutes: '',
              durationSeconds: '',
            },
          ],
          repetitions: '1',
        },
      },
    })
  })

  it('should edit a profile cycle on EDIT_PROFILE_CYCLE', () => {
    const cycleId = 'cycleId'
    const action = {
      type: 'EDIT_PROFILE_CYCLE',
      payload: { id: cycleId, fields: { repetitions: '5' } },
    }
    const state = {
      unsavedForm: {
        stepType: 'thermocycler',
        orderedProfileItems: [cycleId],
        profileItemsById: {
          [cycleId]: {
            type: PROFILE_CYCLE,
            id: cycleId,
            steps: [],
            repetitions: '1',
          },
        },
      },
    }
    const result = unsavedForm(state, action)
    expect(result).toEqual({
      stepType: 'thermocycler',
      orderedProfileItems: [cycleId],
      profileItemsById: {
        [cycleId]: {
          type: PROFILE_CYCLE,
          id: cycleId,
          steps: [],
          repetitions: '5',
        },
      },
    })
  })
})

describe('presavedStepForm reducer', () => {
  it('should populate when a new step is added', () => {
    const addStepAction: AddStepAction = {
      type: 'ADD_STEP',
      payload: { id: 'someId', stepType: 'transfer' },
    }
    const result = presavedStepForm(null, addStepAction)
    expect(result).toEqual({ stepType: 'transfer' })
  })

  it('should not update when the PRESAVED_STEP_ID terminal item is selected', () => {
    const prevState = { stepType: 'transfer' }
    const action = { type: 'SELECT_TERMINAL_ITEM', payload: PRESAVED_STEP_ID }
    expect(presavedStepForm(prevState, action)).toBe(prevState)
  })

  it('should clear when a different terminal item is selected', () => {
    const prevState = { stepType: 'transfer' }
    const action = { type: 'SELECT_TERMINAL_ITEM', payload: 'otherId' }
    expect(presavedStepForm(prevState, action)).toEqual(null)
  })

  const clearingActions = [
    'CANCEL_STEP_FORM',
    'DELETE_STEP',
    'SAVE_STEP_FORM',
    'SELECT_STEP',
  ]
  clearingActions.forEach(actionType => {
    it(`should clear upon ${actionType}`, () => {
      const prevState = { id: 'someId', stepType: 'transfer' }
      expect(presavedStepForm(prevState, { type: actionType })).toEqual(null)
    })
  })
})

redpajama_set_name: RedPajamaGithub